將前景服務遷移至使用者啟動的資料移轉工作

Android 14 針對應用程式可使用前景服務的情況,實施嚴格的規定

此外,我們也在 Android 14 中推出全新的 API,用於指明工作必須為使用者啟動的資料移轉工作。如果應用程式需要執行由使用者啟動的長時間轉移資料程序,例如從遠端伺服器下載檔案,這個 API 就能派上用場。這些工作類型應使用由使用者啟動的資料移轉工作。

顧名思義,使用者啟動的資料移轉工作是由使用者所發起的。這些工作會要求傳送通知並且要立即開始執行,如果系統條件允許,還可能會長時間執行。您可以同時執行多項使用者啟動的資料移轉工作。

當使用者具備應用程式的瀏覽權限時 (或符合任一許可條件),則必須為使用者啟動的工作進行排程。滿足所有限制後,作業系統就能執行使用者啟動的工作,具體情況取決於系統健康狀態限制。此外,系統也能根據所提供的預估酬載大小,判斷執行工作的時間長度。

使用者啟動的資料移轉作業權限

使用者啟動的資料移轉作業需要新權限才能執行:RUN_USER_INITIATED_JOBS。系統會自動授予此權限。 如果您未在應用程式資訊清單中宣告該權限,系統會擲回 SecurityException

為使用者啟動的資料移轉作業排程程序

To run a user initiated job, do the following:

  1. If this is your first time declaring an API with JobScheduler, declare the JobService and associated permissions in your manifest. Also, define a concrete subclass of JobService for your data transfer:

    <service android:name="com.example.app.CustomTransferService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false">
            ...
    </service>
    
    class CustomTransferService : JobService() {
      ...
    }
    
  2. Declare the RUN_USER_INITIATED_JOBS permission in your manifest:

    <manifest ...>
        <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />
        <application ...>
            ...
        </application>
    </manifest>
    
  3. Call the new setUserInitiated() method when building a JobInfo object. It is also recommended that you offer a payload size estimate by calling setEstimatedNetworkBytes() while creating your job:

    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. Schedule the job before the transfer starts, while the application is visible or in the allowed conditions list:

    val jobScheduler: JobScheduler =
        context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    jobScheduler.schedule(jobInfo)
    
  5. When the job is being executed, ensure you call setNotification() on the JobService object. This value is used to make the user aware that the job is running, both in the Task Manager and in the status bar notification area:

    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. Periodically update the notification to keep the user informed of the job's status and progress. If you cannot determine the transfer size ahead of scheduling the job, or need to update the estimated transfer size, use the new API, updateEstimatedNetworkBytes() to update the transfer size after it becomes known.

  7. When execution is complete, call jobFinished() to signal to the system that the job is complete, or that the job should be rescheduled.

可停止使用者啟動的資料移轉作業

使用者和系統均可停止由使用者啟動的移轉作業。

由工作管理員的使用者執行

使用者可以停止在工作管理員中顯示的使用者啟動資料移轉作業。

當使用者按下停止 時,系統會執行以下操作:

  • 立即終止應用程式的處理程序,包括所有其他執行中的工作或前景服務。
  • 不會針對任何執行中的工作呼叫 onStopJob()
  • 防止使用者開放瀏覽權限的工作重新排程。

基於上述理由,建議您針對工作發布的通知中提供控制項,以便有條理地停止工作並重新排程。

請注意,在特殊情況下,工作管理員中所列的工作旁不會顯示停止按鈕,抑或工作完全沒有顯示在工作管理員中。

由系統執行

與一般工作不同的是,使用者啟動的資料移轉工作不會受到應用程式待命值區配額的影響。然而,若發生下列任一情況,系統也會停止工作:

  • 某項限制不再符合開發人員定義的條件。
  • 系統判定工作的執行時間比完成資料移轉工作所需的時間還要長。
  • 系統會以系統健全狀態為優先考量,有鑑於溫度不斷升高,必須停止執行中的工作。
  • 應用程式處理程序因裝置記憶體不足而終止。

當系統停止工作 (但不是因為記憶體不足) 時,系統會呼叫 onStopJob(),並在系統認定的最佳時間點重新執行工作。請確認即使系統未呼叫 onStopJob(),應用程式也可保持資料移轉狀態,並且確認再次呼叫 onStartJob() 後,應用程式可恢復此狀態。

可對使用者啟動的資料移轉作業進行排程的情況

應用程式必須在開放瀏覽權限的視窗中,或符合特定條件時,才能夠啟動使用者啟動的資料移轉作業。為確定將使用者啟動的資料移轉作業排入排程的時間,系統會套用允許應用程式在特殊情況下自背景啟動活動的同一份條件清單。值得注意的是,此清單條件同於從背景啟動前景服務限制的豁免條件組合。

上一個陳述的例外狀況如下:

  • 如果應用程式可以從背景啟動活動,則也將可以從背景啟動使用者啟動的資料移轉作業。
  • 如果應用程式只是在近期活動畫面的現有工作中,存在返回堆疊活動,則無法使系統允許執行使用者啟動的資料移轉作業。

若排定於許可條件清單以外的時間執行作業,則該作業將不會成功執行,且系統會傳回 RESULT_FAILURE 錯誤代碼。

使用者啟動的資料移轉作業許可的限制

To support jobs running at optimal points, Android offers the ability to assign constraints to each job type. These constraints are already available as of Android 13.

Note: The following table only compares the constraints that vary between each job type. See JobScheduler developer page or work constraints for all constraints.

The following table shows the different job types that support a given job constraint, as well as the set of job constraints that WorkManager supports. Use the search bar before the table to filter the table by the name of a job constraint method.

These are the constraints allowed with user-initiated data transfer jobs:

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

測試

以下清單說明手動測試應用程式工作的若干步驟:

  • 如要取得工作 ID,請取得在建構工作時所定義的值。
  • 如要立即執行工作或重試已停止的工作,請在終端機視窗中執行下列指令:

    adb shell cmd jobscheduler run -f APP_PACKAGE_NAME JOB_ID
    
  • 如要模擬系統因穩健性或超出配額問題,強制停止工作執行的狀況,請在終端機視窗中執行下列指令:

    adb shell cmd jobscheduler timeout TEST_APP_PACKAGE TEST_JOB_ID