Dienste im Vordergrund zu vom Nutzer initiierten Datenübertragungsjobs migrieren

Unter Android 14 gelten strenge Regeln dafür, wann Apps Dienste im Vordergrund verwenden dürfen.

In Android 14 führen wir außerdem eine neue API ein, die angibt, dass ein Job ein vom Nutzer initiierter Datenübertragungsjob sein muss. Diese API ist für Anwendungsfälle hilfreich, in denen eine längere, vom Nutzer initiierte Datenübertragung erforderlich ist, z. B. das Herunterladen einer Datei von einem Remoteserver. Für diese Aufgabentypen sollte ein vom Nutzer initiierter Datenübertragungsjob verwendet werden.

Vom Nutzer initiierte Datenübertragungsjobs werden vom Nutzer gestartet. Diese Jobs erfordern eine Benachrichtigung, werden sofort gestartet und können möglicherweise über einen längeren Zeitraum ausgeführt werden, wenn die Systembedingungen es zulassen. Sie können mehrere vom Nutzer initiierte Datenübertragungsjobs gleichzeitig ausführen.

Vom Nutzer initiierte Jobs müssen geplant werden, während die Anwendung für den Nutzer sichtbar ist (oder eine der zulässigen Bedingungen erfüllt). Wenn alle Einschränkungen erfüllt sind, können vom Nutzer initiierte Jobs vom Betriebssystem ausgeführt werden, vorbehaltlich der Einschränkungen des Systemzustands. Das System kann auch die angegebene geschätzte Nutzlastgröße verwenden, um zu bestimmen, wie lange der Job ausgeführt wird.

Berechtigung für vom Nutzer initiierte Datenübertragungsjobs

Für vom Nutzer initiierte Datenübertragungsjobs ist eine neue Berechtigung zum Ausführen erforderlich: RUN_USER_INITIATED_JOBS. Das System erteilt diese Berechtigung automatisch. Das System gibt einen SecurityException aus, wenn du die Berechtigung nicht in deinem App-Manifest deklariert hast.

Prozess zum Planen von vom Nutzer initiierten Datenübertragungsjobs

So führen Sie einen vom Nutzer initiierten Job aus:

  1. Wenn Sie zum ersten Mal eine API mit JobScheduler deklarieren, deklarieren Sie JobService und die zugehörigen Berechtigungen in Ihrem Manifest. Definieren Sie außerdem eine konkrete abgeleitete Klasse von JobService für die Datenübertragung:

    <service android:name="com.example.app.CustomTransferService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false">
            ...
    </service>
    
    class CustomTransferService : JobService() {
      ...
    }
    
  2. Deklariere die Berechtigung „RUN_USER_INITIATED_JOBS“ in deinem Manifest:

    <manifest ...>
        <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />
        <application ...>
            ...
        </application>
    </manifest>
    
  3. Rufen Sie die neue setUserInitiated()-Methode auf, wenn Sie ein JobInfo-Objekt erstellen. Es wird auch empfohlen, eine Schätzung der Nutzlastgröße anzubieten. Dazu rufen Sie beim Erstellen des Jobs setEstimatedNetworkBytes() auf:

    val networkRequestBuilder = NetworkRequest.Builder()
            .addCapability(NET_CAPABILITY_INTERNET)
            .addCapability(NET_CAPABILITY_NOT_METERED)
            // Add or remove capabilities based on your requirements
            .build()
    
    val jobInfo = JobInfo.Builder()
            // ...
            .setUserInitiated(true)
            .setRequiredNetwork(networkRequestBuilder.build())
            .setEstimatedNetworkBytes(1024 * 1024 * 1024)
            // ...
            .build()
    
  4. Planen Sie den Job vor dem Start der Übertragung, während die Anwendung sichtbar ist oder in der Liste der zulässigen Bedingungen aufgeführt ist:

    val jobScheduler: JobScheduler =
        context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    jobScheduler.schedule(jobInfo)
    
  5. Wenn der Job ausgeführt wird, müssen Sie setNotification() für das JobService-Objekt aufrufen. Mit diesem Wert wird sowohl im Task-Manager als auch im Benachrichtigungsbereich der Statusleiste darauf hingewiesen, dass der Job ausgeführt wird:

    class CustomTransferService : JobService() {
      override fun onStartJob(params: JobParameters?): Boolean {
          val notification = Notification.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
                  .setContentTitle("My user-initiated data transfer job")
                  .setSmallIcon(android.R.mipmap.myicon)
                  .setContentText("Job is running")
                  .build()
    
          setNotification(params, notification.id, notification,
                  JobService.JOB_END_NOTIFICATION_POLICY_DETACH)
          // Do the job execution.
      }
    }
    
  6. Aktualisieren Sie die Benachrichtigung regelmäßig, um den Nutzer über den Status und den Fortschritt des Jobs zu informieren. Wenn Sie die Übertragungsgröße vor dem Planen des Jobs nicht ermitteln können oder die geschätzte Übertragungsgröße aktualisieren müssen, verwenden Sie die neue API updateEstimatedNetworkBytes(), um die Übertragungsgröße zu aktualisieren, nachdem sie bekannt ist.

  7. Wenn die Ausführung abgeschlossen ist, rufen Sie jobFinished() auf, um dem System zu signalisieren, dass der Job abgeschlossen oder neu geplant werden soll.

Vom Nutzer initiierte Datenübertragungsjobs können angehalten werden

Sowohl der Nutzer als auch das System können vom Nutzer initiierte Übertragungsjobs stoppen.

Vom Nutzer aus dem Task-Manager

Der Nutzer kann einen vom Nutzer initiierten Datenübertragungsjob stoppen, der im Task-Manager angezeigt wird.

Wenn der Nutzer auf Stopp drückt, geht das System so vor:

  • Beendet den Prozess der Anwendung sofort, einschließlich aller anderen Jobs oder Dienste im Vordergrund, die ausgeführt werden.
  • Ruft onStopJob() für laufende Jobs nicht auf.
  • Verhindert, dass für Nutzer sichtbare Jobs verschoben werden.

Aus diesen Gründen wird empfohlen, in der Benachrichtigung, die für den Job veröffentlicht wird, Steuerelemente bereitzustellen, damit der Job ordnungsgemäß beendet und neu geplant werden kann.

Beachten Sie, dass unter besonderen Bedingungen die Schaltfläche Beenden neben dem Job im Task-Manager nicht angezeigt wird oder der Job im Task-Manager überhaupt nicht angezeigt wird.

Durch das System

Im Gegensatz zu normalen Jobs sind vom Nutzer initiierte Datenübertragungsjobs von Kontingenten für App-Standby-Buckets nicht betroffen. Das System beendet den Job jedoch weiterhin, wenn eine der folgenden Bedingungen eintritt:

  • Eine vom Entwickler definierte Einschränkung wird nicht mehr erfüllt.
  • Das System stellt fest, dass der Job länger als nötig ausgeführt wurde, um die Datenübertragungsaufgabe abzuschließen.
  • Das System muss den Systemzustand priorisieren und Jobs aufgrund eines erhöhten Thermalzustands beenden.
  • Der App-Prozess wird aufgrund des geringen Gerätespeichers abgebrochen.

Wenn der Job vom System (nicht aufgrund des geringen Arbeitsspeichers) beendet wird, ruft das System onStopJob() auf und wiederholt den Job zu einem Zeitpunkt, den das System für optimal hält. Prüfen Sie, ob die Anwendung den Datenübertragungsstatus beibehalten kann, auch wenn onStopJob() nicht aufgerufen wird, und ob die Anwendung diesen Status wiederherstellen kann, wenn onStartJob() noch einmal aufgerufen wird.

Zulässige Bedingungen für die Planung von vom Nutzer initiierten Datenübertragungsjobs

Anwendungen können einen vom Nutzer initiierten Datenübertragungsjob nur starten, wenn sich die Anwendung im sichtbaren Fenster befindet oder bestimmte Bedingungen erfüllt sind. Das System wendet dieselbe Liste von Bedingungen an, die es Apps ermöglichen, in Sonderfällen eine Aktivität aus dem Hintergrund zu starten, um festzustellen, wann ein vom Nutzer initiierter Datenübertragungsjob geplant werden kann. Diese Liste von Bedingungen ist nicht mit den Ausnahmen für Einschränkungen im Vordergrund identisch, die im Hintergrund gestartet wurden.

Es gelten folgende Ausnahmen:

  • Wenn eine App Aktivitäten im Hintergrund starten kann, kann sie auch vom Nutzer initiierte Datenübertragungsjobs im Hintergrund starten.
  • Wenn eine Anwendung im Back-Stack einer vorhandenen Aufgabe auf dem Bildschirm Recents (Zuletzt verwendet) eine Aktivität enthält, kann damit kein vom Nutzer initiierter Datenübertragungsjob ausgeführt werden.

Wenn der Job zu einem Zeitpunkt geplant wurde, der nicht in der Liste der zulässigen Bedingungen aufgeführt ist, schlägt er fehl und gibt den Fehlercode RESULT_FAILURE zurück.

Zulässige Einschränkungen für vom Nutzer initiierte Datenübertragungsjobs

Damit Jobs an optimalen Punkten ausgeführt werden können, bietet Android die Möglichkeit, jedem Jobtyp Einschränkungen zuzuweisen. Diese Einschränkungen sind bereits ab Android 13 verfügbar.

Hinweis: In der folgenden Tabelle werden nur die Einschränkungen verglichen, die je nach Jobtyp variieren. Alle Einschränkungen finden Sie auf der Entwicklerseite "JobScheduler" oder unter Arbeitseinschränkungen.

In der folgenden Tabelle finden Sie die verschiedenen Arten von Dienstleistungen, die eine bestimmte Jobeinschränkung unterstützen, sowie die von WorkManager unterstützten Jobeinschränkungen. Mit der Suchleiste vor der Tabelle können Sie die Tabelle nach dem Namen einer Jobeinschränkungsmethode filtern.

Folgende Einschränkungen sind für vom Nutzer initiierte Datenübertragungsjobs zulässig:

  • setBackoffCriteria(JobInfo.BACKOFF_POLICY_EXPONENTIAL)
  • setClipData()
  • setEstimatedNetworkBytes()
  • setMinimumNetworkChunkBytes()
  • setPersisted()
  • setNamespace()
  • setRequiredNetwork()
  • setRequiredNetworkType()
  • setRequiresBatteryNotLow()
  • setRequiresCharging()
  • setRequiresStorageNotLow()

Testen

Die folgende Liste enthält einige Schritte zum manuellen Testen der Jobs Ihrer Anwendung:

  • Um die Job-ID zu erhalten, rufen Sie den Wert ab, der beim Erstellen des Jobs definiert wurde.
  • Führen Sie den folgenden Befehl in einem Terminalfenster aus, um einen Job sofort auszuführen oder einen angehaltenen Job noch einmal auszuführen:

    adb shell cmd jobscheduler run -f APP_PACKAGE_NAME JOB_ID
    
  • Führen Sie den folgenden Befehl in einem Terminalfenster aus, um das Beenden eines Jobs durch das System (aufgrund des Systemzustands oder aufgrund von Bedingungen außerhalb des Kontingents) zu simulieren:

    adb shell cmd jobscheduler timeout TEST_APP_PACKAGE TEST_JOB_ID