41 votes

Détecter le changement de connectivité dans Android 7 et supérieur lorsque l'application est supprimée / en arrière-plan

Problème:

Donc, le problème est que j'ai une application qui envoie une demande à notre backend chaque fois que le WiFi est connecté (connecté avec le SSID et d'autres infos) ou lorsqu'il est déconnecté (sur le réseau mobile). Cependant, avec les changements dans le Android 7/N et au-dessus, CONNECTIVITY_CHANGE et CONNECTIVITY_ACTION de ne plus fonctionner en arrière-plan. Maintenant, dans la plupart des cas, les gens à l'abus de cette émission, et je peux tout à fait comprendre pourquoi le changement a été fait. Cependant, je n'ai aucune idée de comment résoudre ce problème dans l'état actuel.

Maintenant, je ne suis pas beaucoup d'un développeur Android (c'est un plugin Cordova) donc je compte sur vous les gars!

Comportement attendu: App est réveillé et la demande est envoyée à chaque fois que WiFi commutateurs de la connectivité, même lorsque l'application est tué/en arrière-plan.

Comportement actuel: App n'envoie la demande lorsque l'application est au premier plan.

Essayé jusqu'à présent: Jusqu'à présent, j'ai déplacé l'intention implicite pour écouter CONNECTIVITY_ACTION du manifeste pour enregistrer manuellement dans la partie principale de l'application (plugin). Cela permet de travailler aussi longtemps que l'application est en mémoire, mais pas sur le démarrage à froid ou de fond réelle

Déjà regardé: La plupart des réponses à parler à l'aide de tâches planifiées pour remplacer le manque de diffusion. Je vois comment cela fonctionne pour, par exemple, une nouvelle tentative de téléchargement ou similaire, mais pas pour mon cas (mais s'il vous plaît corrigez-moi si je me trompe). Ci-dessous sont les postes que j'ai déjà regardé:

Détecter les modifications de connectivité sur Android 7.0 Nougat lorsque l'application est en arrière plan

ConnectivityManager.CONNECTIVITY_ACTION obsolète

Détecter les modifier à l'aide de JobScheduler

Android O - Détecter les modifier en arrière-plan

66voto

jitesh mohite Points 3119

Nougat et au-Dessus: Nous devons utiliser JobScheduler et JobService pour des Modifications de la Connexion.

Tout ce que je peux diviser en trois étapes.

Registre JobScheduler à l'intérieur de l'activité. Aussi, Commencez JobService( Service pour gérer les rappels de l'JobScheduler. Les demandes prévues avec le JobScheduler finalement atterrir sur ce service "onStartJob" la méthode.)

public class NetworkConnectionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network_connection);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        scheduleJob();

    }


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void scheduleJob() {
        JobInfo myJob = new JobInfo.Builder(0, new ComponentName(this, NetworkSchedulerService.class))
                .setRequiresCharging(true)
                .setMinimumLatency(1000)
                .setOverrideDeadline(2000)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                .setPersisted(true)
                .build();

        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(myJob);
    }

    @Override
    protected void onStop() {
        // A service can be "started" and/or "bound". In this case, it's "started" by this Activity
        // and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
        // to stopService() won't prevent scheduled jobs to be processed. However, failing
        // to call stopService() would keep it alive indefinitely.
        stopService(new Intent(this, NetworkSchedulerService.class));
        super.onStop();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Start service and provide it a way to communicate with this class.
        Intent startServiceIntent = new Intent(this, NetworkSchedulerService.class);
        startService(startServiceIntent);
    }
}

Le service de commencer et de finir le travail.

public class NetworkSchedulerService extends JobService implements
        ConnectivityReceiver.ConnectivityReceiverListener {

    private static final String TAG = NetworkSchedulerService.class.getSimpleName();

    private ConnectivityReceiver mConnectivityReceiver;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service created");
        mConnectivityReceiver = new ConnectivityReceiver(this);
    }



    /**
     * When the app's NetworkConnectionActivity is created, it starts this service. This is so that the
     * activity and this service can communicate back and forth. See "setUiCallback()"
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        return START_NOT_STICKY;
    }


    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i(TAG, "onStartJob" + mConnectivityReceiver);
        registerReceiver(mConnectivityReceiver, new IntentFilter(Constants.CONNECTIVITY_ACTION));
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i(TAG, "onStopJob");
        unregisterReceiver(mConnectivityReceiver);
        return true;
    }

    @Override
    public void onNetworkConnectionChanged(boolean isConnected) {
        String message = isConnected ? "Good! Connected to Internet" : "Sorry! Not connected to internet";
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();

    }
}

Enfin, La classe de récepteur qui vérifie la connexion réseau les changements.

public class ConnectivityReceiver extends BroadcastReceiver {

    private ConnectivityReceiverListener mConnectivityReceiverListener;

    ConnectivityReceiver(ConnectivityReceiverListener listener) {
        mConnectivityReceiverListener = listener;
    }


    @Override
    public void onReceive(Context context, Intent intent) {
        mConnectivityReceiverListener.onNetworkConnectionChanged(isConnected(context));

    }

    public static boolean isConnected(Context context) {
        ConnectivityManager cm = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    }

    public interface ConnectivityReceiverListener {
        void onNetworkConnectionChanged(boolean isConnected);
    }
}

N'oubliez pas d'ajouter la permission et le service à l'intérieur du fichier de manifeste.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.yourpackagename">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>


    <!-- Always required on api < 21, needed to keep a wake lock while your job is running -->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- Required on api < 21 if you are using setRequiredNetworkType(int) -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- Required on all api levels if you are using setPersisted(true) -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".connectivity.NetworkConnectionActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>


        <!-- Define your service, make sure to add the permision! -->
        <service
            android:name=".connectivity.NetworkSchedulerService"
            android:exported="true"
            android:permission="android.permission.BIND_JOB_SERVICE"/>
    </application>

</manifest>

Veuillez consulter les liens ci-dessous pour plus d'info.

https://github.com/jiteshmohite/Android-Network-Connectivity

https://github.com/evant/JobSchedulerCompat

https://github.com/googlesamples/android-JobScheduler

https://medium.com/@iiro.krankka/its-time-to-kiss-goodbye-to-your-implicit-broadcastreceivers-eefafd9f4f8a

6voto

Guruprasad Points 678

La meilleure façon de saisir la modification de la connectivité Android OS 7 et versions ultérieures consiste à enregistrer votre émission ConnectivityReceiver dans la classe d'application comme ci-dessous. Cela vous permet également d'obtenir des modifications en arrière-plan jusqu'à ce que votre application soit active.

 public class MyApplication extends Application {

      private ConnectivityReceiver connectivityReceiver;

      private ConnectivityReceiver getConnectivityReceiver() {
          if (connectivityReceiver == null)
               connectivityReceiver = new ConnectivityReceiver();

          return connectivityReceiver;
       }
       @Override
       public void onCreate() {
         super.onCreate();
         registerConnectivityReceiver();
       }

     // register here your filtters 
     private void registerConnectivityReceiver(){
       try {
          // if (android.os.Build.VERSION.SDK_INT >= 26) {
          IntentFilter filter = new IntentFilter();
          filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
          //filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
          //filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
          //filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
          registerReceiver(getConnectivityReceiver(), filter);
       } catch (Exception e) {
         MLog.e(TAG, e.getMessage());
       }
 }

}
 

Et puis en manifeste

      <application
      android:name=".app.MyApplication"/>
 

Voici votre ConnectivityReceiver.java

  public class ConnectivityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
      MLog.v(TAG, "onReceive().." + intent.getAction());
      }
    }
 

3voto

Mohit Hooda Points 254

C'est comme ça que je l'ai fait. J'ai créé une méthode IntentService et onCreate et j'ai enregistré networkBroadacst qui vérifie la connexion Internet.

 public class SyncingIntentService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            networkBroadcast=new NetworkBroadcast();
            registerReceiver(networkBroadcast,
                  new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        }
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onHandleIntent(intent);
        return START_STICKY;
    }
}
 

Ceci est ma classe de diffusion

 public class NetworkBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Constants.isInternetConnected(context)) {
//            Toast.makeText(context, "Internet Connect", Toast.LENGTH_SHORT).show();
           context.startService(new Intent(context, SyncingIntentService.class));
        }
        else{}
    }
}
 

De cette façon, vous pouvez vérifier la connexion Internet pour savoir si votre application est au premier plan ou en arrière-plan dans le nougat.

3voto

Akhil Points 2109

Ci-dessous est extrait de la documentation

Les applications ciblant Android 7.0 API (niveau 24) et plus ne reçoivent pas CONNECTIVITY_ACTION émissions s'ils déclarent le récepteur de radiodiffusion dans leur manifeste. Les Apps continuent de recevoir le CONNECTIVITY_ACTION les émissions s'ils s'inscrivent à leur BroadcastReceiver avec Contexte.registerReceiver() et que le contexte est toujours valide.

Ainsi vous obtiendrez de cette Diffusion, jusqu'à ce que votre contexte est valable dans Android N & ci-dessus explicitement à l'enregistrement pour le même.

Démarrage D'Achèvement:

Vous pouvez écouter android.intent.action.BOOT_COMPLETEDdiffusion vous aurez besoin de cette autorisation pour le même.

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

App Tué Scénario:

Vous n'allez pas à la recevoir.

Très attendue, et en raison de diverses raisons

  • Android Oreo a des limitations sur les services en cours d'exécution en arrière-plan, de sorte que vous pouvez faire face à ce sur O appareils

  • Doze mode sur Android Guimauve partir peut provoquer cela, il va arrêter toutes les activités du réseau lui-même et à emporter CPU sillage des serrures

  • Si Doze mode ont un mécanisme pour demander une liste blanche d'applications, cela peut être utile pour vous.

0voto

Frank Nguyen Points 879

Une autre approche qui est plus simple et plus facile lorsque vous utilisez registerNetworkCallback (NetworkRequest, PendingIntent) :

 NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
builder.addTransportType(NetworkCapabilities.TRANSPORT_VPN);

ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Intent intent = new Intent(this, SendAnyRequestService.class);

PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (connectivityManager != null) {
    NetworkRequest networkRequest = builder.build();
    connectivityManager.registerNetworkCallback(networkRequest, pendingIntent);
}
 

Ce qui est SendAnyRequestService.class est votre classe de service et vous pouvez appeler votre API à l'intérieur de celle-ci.

Ce code fonctionne pour Android 6.0 (API 23) et supérieur

Le document de référence est ici

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X