Optimisation en arrière-plan

Les processus en arrière-plan peuvent utiliser beaucoup de mémoire et de batterie. Par exemple, une diffusion implicite peut lancer de nombreux processus en arrière-plan qui ont été enregistrés pour l'écouter, même si ces processus ne sont pas très actifs. Cela peut avoir des répercussions importantes sur les performances des appareils et sur l'expérience utilisateur.

Pour réduire ce problème, Android 7.0 (API 24 niveau) applique les restrictions suivantes :

  • Les applications ciblant Android version 7.0 (niveau 24 d'API) et ultérieures ne reçoivent pas les diffusions CONNECTIVITY_ACTION s'ils déclarent leur récepteur de diffusion dans le fichier manifeste. Les applications continueront à recevoir des diffusions CONNECTIVITY_ACTION si elles enregistrent leur BroadcastReceiver auprès de Context.registerReceiver() et que ce contexte est toujours valide.
  • Les applications ne peuvent pas envoyer ni recevoir de diffusions ACTION_NEW_PICTURE ou ACTION_NEW_VIDEO. Cette optimisation concerne toutes les applications, pas seulement celles qui ciblent Android 7.0 (niveau 24 d'API).

Si votre application utilise l'un de ces intents, supprimez les dépendances dès que possible afin de pouvoir cibler correctement les appareils équipés d'Android version 7.0 ou ultérieure. Le framework Android fournit plusieurs solutions pour réduire le besoin de ces diffusions implicites. Par exemple, JobScheduler et le nouveau WorkManager proposent des mécanismes robustes pour planifier les opérations réseau lorsque certaines conditions, telles qu'une connexion à un réseau non facturé à l'usage, sont remplies. À présent, vous pouvez également utiliser JobScheduler pour réagir aux changements apportés aux fournisseurs de contenu. Les objets JobInfo encapsulent les paramètres utilisés par JobScheduler pour planifier votre tâche. Lorsque les conditions de la tâche sont remplies, le système exécute cette tâche dans le JobService de votre application.

Sur cette page, nous allons apprendre à utiliser d'autres méthodes, telles que JobScheduler, pour adapter votre application à ces nouvelles restrictions.

Restrictions initiées par l'utilisateur

Sur la page Utilisation de la batterie dans les paramètres système, l'utilisateur peut choisir parmi les options suivantes :

  • Illimité : autorise toutes les tâches en arrière-plan, ce qui peut solliciter davantage la batterie.
  • Optimisé (par défaut) : optimise la capacité d'une application à effectuer des tâches en arrière-plan en fonction de la manière dont l'utilisateur interagit avec l'application.
  • Limité : empêche complètement l'exécution d'une application en arrière-plan. Les applications peuvent ne pas fonctionner comme prévu.

Si une application présente certains des comportements insatisfaisants décrits dans Android Vitals, le système peut inviter l'utilisateur à restreindre l'accès de l'application aux ressources système.

Si le système remarque qu'une application consomme trop de ressources, il en informe l'utilisateur et lui donne la possibilité d'en limiter les actions. Cette notification peut apparaître dans les cas suivants :

  • Excès de wakelocks : un wakelock partiel est maintenu pendant une heure lorsque l'écran est éteint.
  • Excès de services d'arrière-plan : si l'application cible des niveaux d'API inférieurs à 26 et a un excès de services en arrière-plan.

Les restrictions précises imposées sont déterminées par le fabricant de l'appareil. Par exemple, sur les builds d'AOSP sous Android version 9 (niveau 28 d'API) ou supérieure, les applications exécutées en arrière-plan qui sont à l'état "Limité" sont soumises aux limites suivantes :

  • Impossible de lancer les services de premier plan.
  • Les services existants au premier plan sont supprimés du premier plan.
  • Les alarmes ne se déclenchent pas.
  • Les tâches ne sont pas exécutées.

Par ailleurs, si une application cible Android version 13 (niveau 33 d'API) ou ultérieure et présente l'état "Limité", le système n'envoie pas la diffusion BOOT_COMPLETED ou LOCKED_BOOT_COMPLETED tant que l'application n'est pas démarrée pour d'autres raisons.

Les restrictions spécifiques sont listées dans Restrictions de gestion de l'alimentation.

Restrictions concernant la réception d'annonces d'activité réseau

Les applications ciblant Android 7.0 (niveau 24 d'API) ne reçoivent pas les annonces CONNECTIVITY_ACTION si elles s'enregistrent pour les recevoir dans leur fichier manifeste. Les processus qui en dépendent ne se lanceront pas. Cela peut poser un problème aux applications qui souhaitent écouter les modifications de réseau ou effectuer des activités réseau groupées lorsque l'appareil se connecte à un réseau non facturé à l'usage. Il existe déjà plusieurs solutions pour contourner cette restriction dans le framework Android, mais choisir la bonne dépend des objectifs que vous fixez pour votre application.

Remarque : Un BroadcastReceiver enregistré auprès de Context.registerReceiver() continue de recevoir ces annonces pendant que l'application est en cours d'exécution.

Planifier les tâches réseau sur des connexions non facturées à l'usage

Lorsque vous utilisez la classe JobInfo.Builder pour compiler votre objet JobInfo, appliquez la méthode setRequiredNetworkType() et transmettez JobInfo.NETWORK_TYPE_UNMETERED en tant que paramètre de tâche. L'exemple de code suivant planifie l'exécution d'un service lorsque l'appareil se connecte à un réseau non facturé à l'usage et est en charge :

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MyJobService::class.java)
    )
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
            .setRequiresCharging(true)
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
      (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo job = new JobInfo.Builder(
    MY_BACKGROUND_JOB,
    new ComponentName(context, MyJobService.class))
      .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
      .setRequiresCharging(true)
      .build();
  js.schedule(job);
}

Lorsque les conditions de votre tâche sont remplies, votre application reçoit un rappel pour exécuter la méthode onStartJob() dans le fichier JobService.class spécifié. Pour voir d'autres exemples d'intégration de JobScheduler, consultez l'application exemple JobScheduler.

WorkManager est une nouvelle alternative à JobScheduler. Il s'agit d'une API qui vous permet de planifier les tâches en arrière-plan qui doivent être terminées, que l'application soit en cours ou non. WorkManager choisit la méthode appropriée pour exécuter la tâche (soit directement sur un thread dans votre application, soit à l'aide de JobScheduler, FirebaseJobDispatcher ou AlarmManager) en fonction de facteurs tels que le niveau d'API de l'appareil. De plus, WorkManager n'a pas besoin des services Play et fournit plusieurs fonctionnalités avancées, comme le chaînage de tâches ou la vérification de l'état d'une tâche. Pour en savoir plus, consultez WorkManager.

Surveiller la connectivité réseau pendant l'exécution de l'application

Les applications en cours d'exécution peuvent toujours écouter CONNECTIVITY_CHANGE avec un BroadcastReceiver enregistré. Cependant, l'API ConnectivityManager fournit une méthode plus robuste pour demander un rappel uniquement lorsque les conditions du réseau précisées sont remplies.

Les objets NetworkRequest définissent les paramètres du rappel réseau en termes de NetworkCapabilities. Créez des objets NetworkRequest avec la classe NetworkRequest.Builder. registerNetworkCallback() transmet ensuite l'objet NetworkRequest au système. Lorsque les conditions du réseau sont remplies, l'application reçoit un rappel pour exécuter la méthode onAvailable() définie dans sa classe ConnectivityManager.NetworkCallback.

L'application continue de recevoir des rappels jusqu'à ce qu'elle se ferme ou qu'elle appelle unregisterNetworkCallback().

Restrictions concernant la réception d'annonces images et vidéos

Sous Android 7.0 (niveau 24 d'API), les applications ne peuvent pas envoyer ni recevoir d'annonces ACTION_NEW_PICTURE ou ACTION_NEW_VIDEO. Cette restriction permet de réduire les performances et les répercussions sur l'expérience utilisateur lorsque plusieurs applications doivent être activées pour traiter une nouvelle image ou vidéo. Android 7.0 (niveau 24 d'API) étend JobInfo et JobParameters afin de fournir une autre solution.

Déclencher des tâches en cas de modification de l'URI de contenu

Pour déclencher des tâches en cas de modifications de l'URI de contenu, Android 7.0 (niveau 24 d'API) étend l'API JobInfo avec les méthodes suivantes :

JobInfo.TriggerContentUri()
Encapsule les paramètres requis pour déclencher une tâche en cas de modification de l'URI de contenu.
JobInfo.Builder.addTriggerContentUri()
Transmet un objet TriggerContentUri à JobInfo. Un objet ContentObserver surveille l'URI de contenu encapsulé. Si plusieurs objets TriggerContentUri sont associés à une tâche, le système propose un rappel même s'il signale un changement dans un seul des URI de contenu.
Ajoute l'option TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS pour déclencher la tâche si des descendants de l'URI donné changent. Cet indicateur correspond au paramètre notifyForDescendants transmis à registerContentObserver().

Remarque : TriggerContentUri() ne peut être utilisé avec setPeriodic() ou setPersisted(). Pour surveiller en permanence les modifications de contenu, planifiez une nouvelle JobInfo avant que le JobService de l'application n'ait fini de traiter le rappel le plus récent.

L'exemple de code suivant programme une tâche, MEDIA_URI, pour qu'elle se déclenche lorsque le système signale une modification dans l'URI de contenu :

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MediaContentJob::class.java)
    )
            .addTriggerContentUri(
                    JobInfo.TriggerContentUri(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
                    )
            )
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
          (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo.Builder builder = new JobInfo.Builder(
          MY_BACKGROUND_JOB,
          new ComponentName(context, MediaContentJob.class));
  builder.addTriggerContentUri(
          new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
          JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
  js.schedule(builder.build());
}

Lorsque le système signale une modification des URI de contenu spécifiés, votre application reçoit un rappel et un objet JobParameters est transmis à la méthode onStartJob() dans MediaContentJob.class.

Déterminer les autorités de contenu qui ont déclenché une tâche

Android 7.0 (niveau 24 d'API) étend également JobParameters pour permettre à votre application de recevoir des informations utiles sur les autorités de contenu et sur les URI qui ont déclenché la tâche :

Uri[] getTriggeredContentUris()
Renvoie un tableau d'URI ayant déclenché la tâche. Ce champ est null si soit aucun URI n'a déclenché la tâche (par exemple, la tâche a été déclenchée en raison d'un délai ou pour une autre raison), soit si le nombre d'URI modifiés est supérieur à 50.
String[] getTriggeredContentAuthorities()
Renvoie un tableau de chaînes d'autorités de contenu ayant déclenché la tâche. Si le tableau renvoyé n'est pas null, utilisez getTriggeredContentUris() pour récupérer les détails des URI modifiés.

L'exemple de code suivant ignore la méthode JobService.onStartJob() et enregistre les autorités de contenu et les URI ayant déclenché la tâche :

Kotlin

override fun onStartJob(params: JobParameters): Boolean {
    StringBuilder().apply {
        append("Media content has changed:\n")
        params.triggeredContentAuthorities?.also { authorities ->
            append("Authorities: ${authorities.joinToString(", ")}\n")
            append(params.triggeredContentUris?.joinToString("\n"))
        } ?: append("(No content)")
        Log.i(TAG, toString())
    }
    return true
}

Java

@Override
public boolean onStartJob(JobParameters params) {
  StringBuilder sb = new StringBuilder();
  sb.append("Media content has changed:\n");
  if (params.getTriggeredContentAuthorities() != null) {
      sb.append("Authorities: ");
      boolean first = true;
      for (String auth :
          params.getTriggeredContentAuthorities()) {
          if (first) {
              first = false;
          } else {
             sb.append(", ");
          }
           sb.append(auth);
      }
      if (params.getTriggeredContentUris() != null) {
          for (Uri uri : params.getTriggeredContentUris()) {
              sb.append("\n");
              sb.append(uri);
          }
      }
  } else {
      sb.append("(No content)");
  }
  Log.i(TAG, sb.toString());
  return true;
}

Optimisez encore votre application

Optimiser vos applications pour qu'elles s'exécutent sur des appareils ou dans des conditions à faible capacité de mémoire peut améliorer les performances et l'expérience utilisateur. Supprimer les dépendances sur les services d'arrière-plan et les broadcast receivers implicites enregistrés dans le fichier manifeste peut aider votre application à mieux fonctionner sur ces appareils. Bien qu'Android 7.0 (niveau 24 d'API) prenne des mesures pour réduire certains de ces problèmes, il est recommandé d'optimiser votre application pour qu'elle s'exécute sans aucun recours aux processus en arrière-plan.

Les commandes Android Debug Bridge (ADB) suivantes peuvent vous aider à tester le comportement de l'application avec les processus en arrière-plan désactivés :

  • Pour simuler des conditions dans lesquelles les diffusions implicites et les services d'arrière-plan ne sont pas disponibles, saisissez la commande suivante :
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Pour réactiver les diffusions implicites et les services d'arrière-plan, saisissez la commande suivante :
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Vous pouvez simuler l'état "Limité" pour l'utilisation de la batterie en arrière-plan. Ce paramètre empêche votre application de s'exécuter en arrière-plan. Pour ce faire, exécutez la commande suivante dans une fenêtre de terminal :
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny