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

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 按钮不会显示在任务管理器中的作业旁边,或者该作业根本不会显示在任务管理器中。

由系統執行

Unlike regular jobs, user-initiated data transfer jobs are unaffected by App Standby Buckets quotas. However, the system still stops the job if any of the following conditions occur:

  • A developer-defined constraint is no longer met.
  • The system determines that the job has run for longer than necessary to complete the data transfer task.
  • The system needs to prioritize system health and stop jobs due to increased thermal state.
  • The app process is killed due to low device memory.

When the job is stopped by the system (not by the low-memory case), the system calls onStopJob(), and the system retries the job at a time that the system deems to be optimal. Check that your app can persist data transfer state, even if onStopJob() isn't called, and that your app can restore this state when onStartJob() is called again.

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

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

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

  • 如果應用程式可以從背景啟動活動,則也將可以從背景啟動使用者啟動的資料移轉作業。
  • 如果應用程式現有工作的返回堆疊中存在活動,在「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