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

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() 以告知系統工作已經完成,或應重新為工作排程。

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

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

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

用户可以停止显示在任务管理器中的用户发起的传输作业。

在用户按 Stop 时,系统会执行以下操作:

  • 立即终止应用的进程,包括正在运行的所有其他作业或前台服务。
  • 不针对任何正在运行的作业调用 onStopJob()
  • 阻止重新调度用户可见的作业。

因此,建议在发布的作业通知中提供控件,以便顺利停止和重新调度作业。

请注意,在特殊情况下,Stop 按钮不会显示在任务管理器中的作业旁边,或者该作业根本不会显示在任务管理器中。

由系統執行

与常规作业不同,用户发起的数据传输作业不受应用待机模式存储分区配额的影响。但是,如果出现以下任一情况,系统仍会停止作业:

  • 不再满足开发者定义的约束条件。
  • 系统确定该作业的运行时间超出了完成数据传输任务所需的时间。
  • 系统需要优先考虑系统运行状况,并因发热程度上升而停止作业。
  • 应用进程因设备内存不足而被终止。

系统停止作业(并非因为内存不足)时,系统会调用 onStopJob(),系统会在系统认为最佳的时间重试作业。检查您的应用是否可以保留数据传输状态(即使未调用 onStopJob()),并且您的应用可以在再次调用 onStartJob() 时恢复此状态。

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

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

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

  • 如果應用程式可以從背景啟動活動,則也將可以從背景啟動使用者啟動的資料移轉作業。
  • 如果應用程式現有工作的返回堆疊中存在活動,在「Recents」畫面上,就無法執行使用者啟動的資料移轉作業。

若排定於許可條件清單以外的時間執行作業,則該作業將不會成功執行,且系統會傳回 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