web-dev-qa-db-fra.com

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

Problème:

Le problème est donc que j'ai une application qui envoie une requête à notre serveur dès que le WiFi est connecté (avec le SSID connecté et d'autres informations) ou lorsqu'il est déconnecté (sur le réseau mobile). Toutefois, avec les modifications apportées dans Android 7/N et les versions ultérieures, CONNECTIVITY_CHANGE et CONNECTIVITY_ACTION ne fonctionnent plus en arrière-plan. Maintenant, dans la plupart des cas, les gens abusent de cette émission et, à ce titre, je peux parfaitement comprendre pourquoi le changement a été apporté. Cependant, je ne sais pas comment résoudre ce problème dans l'état actuel.

Maintenant, je ne suis pas du tout un développeur Android (il s’agit d’un plugin Cordova), alors je compte sur vous!

Comportement attendu: L'application est réveillée et une demande est envoyée à chaque fois que le WiFi change de connectivité, même si l'application est arrêtée/en arrière-plan.

Comportement actuel: L'application n'envoie une demande que lorsque l'application est au premier plan.

Essayé jusqu'à présent: Jusqu'à présent, j'ai déplacé l'intention implicite d'écouter CONNECTIVITY_ACTION du manifeste pour l'enregistrer manuellement dans la partie principale de l'application ( brancher). Cela le fait fonctionner tant que l'application est en mémoire mais pas au démarrage à froid ou en arrière-plan

Déjà regardé: La plupart des réponses parlent d'utiliser des tâches planifiées pour remplacer la diffusion manquante. Je vois comment cela fonctionne, par exemple, pour réessayer un téléchargement ou similaire, mais pas pour mon cas (mais corrigez-moi si je me trompe). Ci-dessous, les SO messages que j'ai déjà consultés:

Détecter les changements de connectivité sur Android 7.0 Nougat lorsque l'application est au premier plan

ConnectivityManager.CONNECTIVITY_ACTION obsolète

Détecter le changement de connectivité à l'aide de JobScheduler

Android O - Détecter le changement de connectivité en arrière-plan

33
Remy Kabel

Nougat et au-dessus: Nous devons utiliser JobScheduler et JobService pour les modifications de connexion.

Tout ce que je peux diviser en trois étapes.

Enregistrez JobScheduler dans l’activité. En outre, Start JobService (Service permettant de gérer les rappels à partir de JobScheduler. Les demandes planifiées avec JobScheduler aboutissent finalement sur la méthode "onStartJob" de ce service.)

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 pour commencer et 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 les changements de connexion réseau.

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 l'autorisation et le service dans le fichier 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>

S'il vous plaît se référer ci-dessous des liens pour plus d'informations.

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

54
jitsm555

La meilleure façon de saisir le changement de connectivité Android Les OS 7 et suivants consistent à enregistrer votre émission ConnectivityReceiver dans la classe Application comme ci-dessous. Cela vous permet également de modifier en arrière-plan les modifications apportées 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());
      }
    }
4
Guruprasad

C'est comme ça que je l'ai fait. J'ai créé une méthode IntentService et dans onCreate et j'ai enregistré networkBroadacst pour vérifier 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.

3
Mohit Hooda

Ci-dessous, extrait de la documentation

Les applications ciblant Android 7.0 (API de niveau 24) et supérieur ne reçoivent pas les diffusions CONNECTIVITY_ACTION si elles déclarent le récepteur de diffusion dans leur manifeste. Les applications recevront toujours les émissions CONNECTIVITY_ACTION si elles enregistrent leur BroadcastReceiver auprès de Context.registerReceiver () et que ce contexte est toujours valide.

Ainsi, vous obtiendrez cette diffusion jusqu'à ce que votre contexte soit valide dans Android N & ci-dessus en vous enregistrant explicitement.

Boot Completed:

Vous pouvez écouter la diffusion Android.intent.action.BOOT_COMPLETED dont vous aurez besoin pour ce faire.

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

Scénario App Killed:

Vous n'allez pas le recevoir.

C'est très attendu et pour différentes raisons

2
Akhil

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);
}

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 les versions ultérieures.

Le document de référence est ici

0
Frank Nguyen