Hintergrundoptimierung

Hintergrundprozesse können speicher- und akkuintensiv sein. So kann eine implizite Übertragung beispielsweise viele Hintergrundprozesse starten, die sich dafür registriert haben, darauf zu warten, auch wenn diese Prozesse nicht viel Arbeit leisten. Dies kann sich sowohl auf die Geräteleistung als auch auf die Nutzerfreundlichkeit erheblich auswirken.

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

Wenn Ihre App einen dieser Intents verwendet, sollten Sie die Abhängigkeiten so schnell wie möglich entfernen, damit Sie Ihre App richtig auf Geräte mit Android 7.0 oder höher ausrichten können. Das Android-Framework bietet mehrere Lösungen, um die Notwendigkeit dieser impliziten Übertragungen zu verringern. JobScheduler und der neue WorkManager bieten beispielsweise robuste Mechanismen zum Planen von Netzwerkaktionen, wenn bestimmte Bedingungen erfüllt sind, z. B. eine Verbindung zu einem unbegrenzten Netzwerk. Sie können JobScheduler jetzt auch verwenden, um auf Änderungen bei Contentanbietern zu 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 auf dem JobService Ihrer App aus.

Auf dieser Seite erfahren Sie, wie Sie mithilfe alternativer Methoden wie JobScheduler Ihre App an diese neuen Einschränkungen anpassen können.

Vom Nutzer initiierte Einschränkungen

Auf der Seite Akkunutzung in den Systemeinstellungen kann der Nutzer zwischen den folgenden Optionen wählen:

  • Uneingeschränkt:Alle Hintergrundaktivitäten werden zugelassen, was den Akkuverbrauch erhöhen kann.
  • Optimiert (Standard): Die Fähigkeit einer App, Hintergrundaktivitäten auszuführen, wird basierend auf der Interaktion des Nutzers mit der App optimiert.
  • Eingeschränkt:Verhindert vollständig, dass eine App im Hintergrund ausgeführt wird. Apps funktionieren möglicherweise nicht wie erwartet.

Wenn eine App einige der in Android Vitals beschriebenen Fehlfunktionen aufweist, wird der Nutzer möglicherweise aufgefordert, den Zugriff dieser App auf Systemressourcen einzuschränken.

Wenn das System feststellt, dass eine App zu viele Ressourcen verbraucht, wird der Nutzer benachrichtigt und kann die Aktionen der App einschränken. Folgende Verhaltensweisen können die Benachrichtigung auslösen:

  • Übermäßige Wakelocks: 1 Teil-Wakelock, das eine Stunde lang gehalten wird, wenn das Display aus ist
  • Zu viele Hintergrunddienste: Wenn die App auf API-Levels unter 26 ausgerichtet ist und zu viele Hintergrunddienste hat

Die genauen Einschränkungen werden vom Gerätehersteller festgelegt. Beispielsweise gelten für Apps, die im Hintergrund ausgeführt werden und den Status „eingeschränkt“ haben, in AOSP-Builds mit Android 9 (API-Level 28) oder höher 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-Broadcasts erst, wenn die App aus anderen Gründen gestartet wird.

Die spezifischen Einschränkungen sind unter Einschränkungen der Energieverwaltung aufgeführt.

Einschränkungen beim Empfang von Übertragungen von Netzwerkaktivitäten

Apps, die auf Android 7.0 (API-Level 24) ausgerichtet sind, erhalten keine CONNECTIVITY_ACTION-Broadcasts, wenn sie sich in ihrem Manifest für den Empfang registrieren. Prozesse, die von diesem Broadcast abhängen, werden nicht gestartet. Das kann ein Problem für Apps darstellen, die auf Netzwerkänderungen reagieren oder Netzwerkaktivitäten im Bulk-Verfahren ausführen möchten, wenn sich das Gerät mit einem unbegrenzten Netzwerk verbindet. Es gibt bereits mehrere Lösungen im Android-Framework, um diese Einschränkung zu umgehen. Welche Sie wählen, hängt davon ab, was Sie mit Ihrer App erreichen möchten.

Hinweis:Ein bei Context.registerReceiver() registrierter BroadcastReceiver empfängt diese Übertragungen weiterhin, während die App ausgeführt wird.

Netzwerkjobs auf unbegrenzten Verbindungen planen

Wenn Sie die Klasse JobInfo.Builder zum Erstellen Ihres JobInfo-Objekts verwenden, wenden Sie die Methode setRequiredNetworkType() an und übergeben Sie JobInfo.NETWORK_TYPE_UNMETERED als Jobparameter. Im folgenden Codebeispiel wird ein Dienst geplant, der ausgeführt wird, wenn sich das Gerät mit einem unbegrenzten Netzwerk verbindet und geladen 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, erhält Ihre App einen Callback, um die Methode onStartJob() im angegebenen JobService.class auszuführen. Weitere Beispiele für die Implementierung von JobScheduler finden Sie in der Beispiel-App „JobScheduler“.

Eine neue Alternative zu JobScheduler ist WorkManager, eine API, mit der Sie Hintergrundaufgaben planen können, die garantiert abgeschlossen werden müssen, unabhängig davon, ob der App-Prozess aktiv ist oder nicht. WorkManager wählt die geeignete Methode zum Ausführen der Arbeit aus (entweder direkt in einem Thread in Ihrem App-Prozess oder mit JobScheduler, FirebaseJobDispatcher oder AlarmManager) basierend auf Faktoren wie der API-Ebene des Geräts. Außerdem sind für WorkManager keine Play-Dienste erforderlich und es bietet mehrere erweiterte Funktionen, z. B. die Verknüpfung von Aufgaben oder das Prüfen des Status einer Aufgabe. Weitere Informationen finden Sie unter WorkManager.

Netzwerkverbindung während der Ausführung der App überwachen

Laufende Apps können weiterhin mit einer registrierten BroadcastReceiver auf CONNECTIVITY_CHANGE warten. Die ConnectivityManager API bietet jedoch eine robustere Methode, um nur dann einen Rückruf anzufordern, wenn bestimmte 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, erhält die App einen Callback, um die in der ConnectivityManager.NetworkCallback-Klasse definierte onAvailable()-Methode auszuführen.

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

Einschränkungen beim 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. Diese Einschränkung hilft, die Auswirkungen auf die Leistung und die Nutzerfreundlichkeit zu verringern, wenn mehrere Apps aktiviert werden müssen, um ein neues Bild oder Video zu verarbeiten. Android 7.0 (API-Level 24) bietet eine alternative Lösung, die JobInfo und JobParameters erweitert.

Jobs bei Änderungen der Inhalts-URI auslösen

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

JobInfo.TriggerContentUri()
Enthält Parameter, die erforderlich sind, um einen Job bei Änderungen des Inhalts-URIs auszulösen.
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, sendet das System einen Rückruf, auch wenn nur eine Änderung an einem der Inhalts-URIs gemeldet wird.
Mit dem Flag TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS wird der Job ausgelöst, wenn sich ein untergeordneter URI ändert. Dieses Flag entspricht dem notifyForDescendants-Parameter, der an registerContentObserver() übergeben wird.

Hinweis:TriggerContentUri() kann nicht mit setPeriodic() oder setPersisted() kombiniert werden. Wenn du kontinuierlich nach Inhaltsänderungen suchen möchtest, plane einen neuen JobInfo, bevor die JobService der App die Verarbeitung des letzten Rückrufs abgeschlossen hat.

Im 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, erhält deine App einen Rückruf und ein JobParameters-Objekt wird an die onStartJob()-Methode in MediaContentJob.class übergeben.

Ermitteln, welche Content-Behörden einen Job ausgelöst haben

Unter Android 7.0 (API-Level 24) wird JobParameters außerdem erweitert, damit Ihre App nützliche Informationen dazu erhält, welche Content-Behörden und URIs den Job ausgelöst haben:

Uri[] getTriggeredContentUris()
Gibt ein Array von URIs zurück, die den Job ausgelöst haben. Dieser Wert ist null, wenn der Job entweder durch keine URIs ausgelöst wurde (z. B. aufgrund eines Termins oder aus einem anderen Grund) oder die Anzahl der geänderten URIs mehr als 50 beträgt.
String[] getTriggeredContentAuthorities()
Gibt ein String-Array mit den Content-Behörden zurück, die den Job ausgelöst haben. Wenn das zurückgegebene Array nicht null ist, verwende getTriggeredContentUris(), um die Details zu den URIs abzurufen, die sich geändert haben.

Im folgenden Beispielcode wird die Methode JobService.onStartJob() überschrieben und die Content-Behörden und URIs erfasst, 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 Apps so optimieren, dass sie auf Geräten mit wenig Arbeitsspeicher oder bei wenig Arbeitsspeicher ausgeführt werden können, lässt sich die Leistung und Nutzerfreundlichkeit verbessern. Wenn Sie Abhängigkeiten von Hintergrunddiensten und im Manifest registrierte implizite Übertragungsempfänger entfernen, kann Ihre App auf solchen Geräten besser funktionieren. Unter Android 7.0 (API-Level 24) werden zwar Maßnahmen ergriffen, um einige dieser Probleme zu reduzieren, wir empfehlen Ihnen jedoch, Ihre App so zu optimieren, dass sie ohne diese Hintergrundprozesse ausgeführt wird.

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

  • Wenn Sie Bedingungen simulieren möchten, unter denen implizite Übertragungen und Hintergrunddienste nicht verfügbar sind, geben Sie den folgenden Befehl ein:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Wenn Sie implizite Übertragungen und Hintergrunddienste wieder aktivieren möchten, geben Sie den folgenden Befehl ein:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Sie können simulieren, dass der Nutzer Ihre App für die Akkunutzung im Hintergrund in den Status „Eingeschränkt“ versetzt. Mit dieser Einstellung kann Ihre App nicht im Hintergrund ausgeführt werden. Führen Sie dazu den folgenden Befehl in einem Terminalfenster aus:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny