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:
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
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
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());
}
}
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.
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
Android Oreo a des limitations sur l'exécution de services en arrière-plan, vous pouvez donc faire face à cela sur des appareils O
Mode assoupi sur Android La guimauve peut causer ceci, il va arrêter toutes les opérations du réseau et prendre les verrous de réveil du processeur
Bien que le mode Doze ait un mécanisme pour demande de liste blanche d'applications , cela peut vous être utile.
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