Hintergrundoptimierung

Hintergrundprozesse können speicher- und energieverbrauchsintensiv sein. Ein impliziter Broadcast kann beispielsweise viele Hintergrundprozesse starten, die zum Abhören registriert wurden, auch wenn diese Prozesse möglicherweise nicht viel Arbeit ausführen. Dies kann erhebliche Auswirkungen auf die Geräteleistung und die Nutzerfreundlichkeit haben.

In Android 7.0 (API-Level 24) gelten die folgenden Einschränkungen, um dieses Problem zu beheben:

Wenn deine App einen dieser Intents verwendet, solltest du die entsprechenden Abhängigkeiten so schnell wie möglich entfernen, damit du die App korrekt auf Geräte mit Android 7.0 oder höher ausrichten kannst. Das Android-Framework bietet mehrere Lösungen, um die Notwendigkeit dieser impliziten Broadcasts zu verringern. Beispielsweise bieten JobScheduler und der neue WorkManager robuste Mechanismen zur Planung von Netzwerkvorgängen, wenn bestimmte Bedingungen, z. B. eine Verbindung zu einem kostenlosen Netzwerk, erfüllt sind. Sie können jetzt auch mit JobScheduler auf Änderungen von Contentanbietern reagieren. JobInfo-Objekte kapseln die Parameter, die JobScheduler zum Planen Ihres Jobs verwendet. Wenn die Bedingungen des Jobs erfüllt sind, führt das System diesen Job im JobService der Anwendung aus.

Auf dieser Seite erfahren Sie, wie Sie mit alternativen Methoden wie JobScheduler Ihre Anwendung an diese neuen Einschränkungen anpassen können.

Vom Nutzer initiierte Einschränkungen

Auf der Seite Akkunutzung in den Systemeinstellungen kann der Nutzer eine der folgenden Optionen auswählen:

  • Uneingeschränkt:Alle Hintergrundarbeiten zulassen, die den Akku stark beanspruchen können.
  • Optimiert (Standardeinstellung): Die Fähigkeit einer App zur Durchführung von Hintergrundarbeiten zu optimieren, je nachdem, wie der Nutzer mit der App interagiert.
  • Eingeschränkt:Verhindert vollständig, dass eine App im Hintergrund ausgeführt wird. Apps funktionieren möglicherweise nicht wie erwartet.

Wenn eine App eines der unter Android Vitals beschriebenen schlechten Verhaltensweisen aufweist, wird der Nutzer möglicherweise vom System aufgefordert, den Zugriff dieser App auf Systemressourcen einzuschränken.

Wenn das System feststellt, dass eine App übermäßig viele Ressourcen beansprucht, wird der Nutzer benachrichtigt und hat die Möglichkeit, die Aktionen der App einzuschränken. Folgende Verhaltensweisen können die Meldung auslösen:

  • Übermäßige Wakelocks: 1 Teil-Wakelock wird für eine Stunde gehalten, wenn das Display aus ist.
  • Übermäßige Hintergrunddienste: Wenn die App auf API-Level unter 26 ausgerichtet ist und übermäßig viele Hintergrunddienste enthält

Die genauen Einschränkungen werden vom Gerätehersteller festgelegt. Bei AOSP-Builds, die Android 9 (API-Level 28) oder höher ausführen, gelten beispielsweise für im Hintergrund ausgeführte Anwendungen mit dem Status „Eingeschränkt“ die folgenden Einschränkungen:

  • Dienste im Vordergrund können nicht gestartet werden
  • Vorhandene Dienste im Vordergrund werden aus dem Vordergrund entfernt
  • Wecker werden nicht ausgelöst
  • Jobs werden nicht ausgeführt

Wenn eine App auf Android 13 (API-Level 33) oder höher ausgerichtet ist und sich im Status „Eingeschränkt“ befindet, sendet das System die BOOT_COMPLETED- oder LOCKED_BOOT_COMPLETED-Übertragung erst dann, wenn die App aus anderen Gründen gestartet wurde.

Die spezifischen Einschränkungen sind unter Einschränkungen für die Energieverwaltung aufgeführt.

Einschränkungen für den Empfang von Broadcasts zu Netzwerkaktivitäten

Apps, die auf Android 7.0 (API-Level 24) ausgerichtet sind, erhalten keine CONNECTIVITY_ACTION-Broadcasts, wenn sie sich dafür in ihrem Manifest registrieren. Außerdem werden Prozesse, die von diesem Broadcast abhängig sind, nicht gestartet. Dies kann ein Problem für Apps darstellen, die auf Netzwerkänderungen warten oder Bulk-Netzwerkaktivitäten ausführen möchten, wenn das Gerät mit einem kostenlosen Netzwerk verbunden ist. Im Android-Framework gibt es bereits mehrere Lösungen, um diese Einschränkung zu umgehen. Die Wahl der richtigen Lösung hängt jedoch davon ab, was Sie mit Ihrer App erreichen möchten.

Hinweis:Eine bei Context.registerReceiver() registrierte BroadcastReceiver empfängt diese Broadcasts weiterhin, während die App ausgeführt wird.

Netzwerkjobs für kostenlose Verbindungen planen

Wenn Sie das JobInfo-Objekt mit der Klasse JobInfo.Builder erstellen, wenden Sie die Methode setRequiredNetworkType() an und übergeben Sie JobInfo.NETWORK_TYPE_UNMETERED als Jobparameter. Mit dem folgenden Codebeispiel wird die Ausführung eines Dienstes geplant, wenn das Gerät mit einem kostenlosen Netzwerk verbunden ist und aufgeladen wird:

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

Wenn die Bedingungen für Ihren Job erfüllt sind, empfängt die Anwendung einen Callback, um die Methode onStartJob() im angegebenen JobService.class auszuführen. Weitere Beispiele für die JobScheduler-Implementierung findest du in der Beispiel-App „JobScheduler“.

Eine neue Alternative zu JobScheduler ist WorkManager, eine API, mit der Sie Hintergrundaufgaben planen können, die eine garantierte Ausführung erfordern, unabhängig davon, ob der Anwendungsprozess in der Nähe ist oder nicht. WorkManager wählt auf Grundlage von Faktoren wie dem API-Level des Geräts die geeignete Methode für die Ausführung der Arbeit aus (entweder direkt in einem Thread in Ihrem App-Prozess oder mithilfe von JobScheduler, FirebaseJobDispatcher oder AlarmManager). Außerdem sind für WorkManager keine Google Play-Dienste erforderlich und es bietet verschiedene erweiterte Funktionen wie das Verketten von Aufgaben oder die Überprüfung des Aufgabenstatus. Weitere Informationen finden Sie unter WorkManager.

Netzwerkkonnektivität überwachen, während die App ausgeführt wird

Ausgeführte Anwendungen können weiterhin mit einer registrierten BroadcastReceiver auf CONNECTIVITY_CHANGE warten. Die ConnectivityManager API bietet jedoch eine robustere Methode, einen Callback nur dann anzufordern, wenn die angegebenen Netzwerkbedingungen erfüllt sind.

NetworkRequest-Objekte definieren die Parameter des Netzwerk-Callbacks in Bezug auf NetworkCapabilities. Sie erstellen NetworkRequest-Objekte mit der Klasse NetworkRequest.Builder. registerNetworkCallback() übergibt dann das NetworkRequest-Objekt an das System. Wenn die Netzwerkbedingungen erfüllt sind, empfängt die App einen Callback, um die Methode onAvailable() auszuführen, die in ihrer ConnectivityManager.NetworkCallback-Klasse definiert ist.

Die App empfängt weiterhin Callbacks, bis sie entweder beendet oder unregisterNetworkCallback() aufgerufen wird.

Einschränkungen für den Empfang von Bild- und Videoübertragungen

Unter Android 7.0 (API-Level 24) können Apps keine ACTION_NEW_PICTURE- oder ACTION_NEW_VIDEO-Broadcasts senden oder empfangen. Durch diese Einschränkung werden die Auswirkungen auf Leistung und Nutzererfahrung verringert, wenn mehrere Anwendungen aktiviert werden müssen, um ein neues Bild oder Video zu verarbeiten. Android 7.0 (API-Level 24) erweitert JobInfo und JobParameters und stellt damit eine alternative Lösung dar.

Jobs bei Änderungen am Inhalts-URI auslösen

Android 7.0 (API-Level 24) erweitert die JobInfo API um die folgenden Methoden, um Jobs bei Änderungen des Inhalts-URI auszulösen:

JobInfo.TriggerContentUri()
Kapselt Parameter, die zum Auslösen eines Jobs bei Änderungen am Inhalts-URI erforderlich sind.
JobInfo.Builder.addTriggerContentUri()
Übergibt ein TriggerContentUri-Objekt an JobInfo. Ein ContentObserver überwacht den URI des gekapselten Inhalts. Wenn einem Job mehrere TriggerContentUri-Objekte zugeordnet sind, stellt das System einen Callback bereit, auch wenn nur eine Änderung nur an einem der Inhalts-URIs erfolgt.
Fügen Sie das Flag TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS hinzu, um den Job auszulösen, wenn sich Nachfolger des angegebenen URIs ändern. Dieses Flag entspricht dem Parameter notifyForDescendants, der an registerContentObserver() übergeben wurde.

Hinweis: TriggerContentUri() kann nicht in Kombination mit setPeriodic() oder setPersisted() verwendet werden. Damit inhaltliche Änderungen kontinuierlich überwacht werden, planen Sie einen neuen JobInfo, bevor der JobService der App die Verarbeitung des letzten Callbacks abgeschlossen hat.

Mit dem folgenden Beispielcode wird ein Job geplant, der ausgelöst wird, wenn das System eine Änderung am Inhalts-URI MEDIA_URI meldet:

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

Wenn das System eine Änderung an den angegebenen Inhalts-URIs meldet, empfängt Ihre App einen Callback und ein JobParameters-Objekt wird an die Methode onStartJob() in MediaContentJob.class übergeben.

Bestimmen, welche Content-Autoren einen Job ausgelöst haben

Android 7.0 (API-Level 24) erweitert außerdem JobParameters, damit deine App nützliche Informationen darüber erhält, welche Content-Zertifizierungsstellen und URIs den Job ausgelöst haben:

Uri[] getTriggeredContentUris()
Gibt ein Array von URIs zurück, die den Job ausgelöst haben. Der Wert ist null, wenn der Job entweder nicht über URIs ausgelöst wurde (z. B. aufgrund einer Frist oder aus einem anderen Grund) oder die Anzahl der geänderten URIs größer als 50 ist.
String[] getTriggeredContentAuthorities()
Gibt ein String-Array mit Content-Zertifizierungsstellen zurück, die den Job ausgelöst haben. Wenn das zurückgegebene Array nicht null ist, verwenden Sie getTriggeredContentUris(), um die Details zu den URIs abzurufen, die geändert wurden.

Der folgende Beispielcode überschreibt die Methode JobService.onStartJob() und zeichnet die Inhaltsstellen und URIs auf, die den Job ausgelöst haben:

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

App weiter optimieren

Wenn Sie Ihre Anwendungen für die Ausführung auf Geräten mit wenig Arbeitsspeicher oder mit wenig Arbeitsspeicher optimieren, können Sie die Leistung und die Nutzerfreundlichkeit verbessern. Das Entfernen von Abhängigkeiten von Hintergrunddiensten und Manifest-registrierten impliziten Broadcast-Empfängern kann dazu beitragen, dass deine App auf solchen Geräten besser läuft. Unter Android 7.0 (API-Level 24) werden zwar einige dieser Probleme reduziert, es wird aber empfohlen, deine App so zu optimieren, dass sie ohne diese Hintergrundprozesse vollständig ausgeführt wird.

Mit den folgenden Android Debug Bridge-Befehlen (ADB) können Sie das App-Verhalten bei deaktivierten Hintergrundprozessen testen:

  • Geben Sie den folgenden Befehl ein, um Bedingungen zu simulieren, in denen implizite Broadcasts und Hintergrunddienste nicht verfügbar sind:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Geben Sie den folgenden Befehl ein, um implizite Broadcasts und Hintergrunddienste wieder zu aktivieren:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Du kannst simulieren, dass Nutzer deine App für die Nutzung des Hintergrundakkus in den Status „Eingeschränkt“ versetzen. Diese Einstellung verhindert, dass Ihre App im Hintergrund ausgeführt werden kann. Führen Sie dazu den folgenden Befehl in einem Terminalfenster aus:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny