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

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

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

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

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

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

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

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

如要執行使用者啟動的作業,請執行下列操作:

  1. 如果這是您第一次使用 JobScheduler 宣告 API,請在資訊清單中宣告 JobService 和相關權限。此外,請定義用於資料移轉的 JobService 具體子類別:

    <service android:name="com.example.app.CustomTransferService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false">
            ...
    </service>
    
    class CustomTransferService : JobService() {
      ...
    }
    
  2. 在資訊清單中宣告 RUN_USER_INITIATED_JOBS 權限:

    <manifest ...>
        <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />
        <application ...>
            ...
        </application>
    </manifest>
    
  3. 建構 JobInfo 物件時,呼叫新的 setUserInitiated() 方法。此外,建議您在建立工作時呼叫 setEstimatedNetworkBytes(),以提供酬載大小預估值:

    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. 在移轉開始前、應用程式可見,或在許可條件清單中安排工作:

    val jobScheduler: JobScheduler =
        context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    jobScheduler.schedule(jobInfo)
    
  5. 執行工作時,請務必在 JobService 物件上呼叫 setNotification()。此值用於告知使用者工作正在工作管理員和狀態列通知區域執行中:

    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. 定期更新通知,讓使用者掌握工作狀態與進度。如果您無法在工作排程前確定移轉大小,或是需要更新預估移轉大小,請使用新的 API updateEstimatedNetworkBytes() 更新已知的移轉大小。

  7. 執行作業完成後,請呼叫 jobFinished() 以告知系統工作已經完成,或應重新為工作排程。

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

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

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

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

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

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

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

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

由系統執行

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

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

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

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

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

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

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

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

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

為支援在最佳時間點執行的工作,Android 為各個工作類型提供指派限制的功能。這些限制自 Android 13 起已可供使用。

附註:下表僅就各種工作類型之間的限制差異提出比較。如要瞭解所有限制,請參閱 JobScheduler 開發人員頁面工作限制條件

下表說明支援特定工作限制的各種工作類型,並列出 WorkManager 支援的工作限制組合。使用表格前的搜尋列,即可按照工作限制方法的名稱篩選表格。

以下限制適用於使用者啟動的資料移轉作業:

  • 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