对于应用何时可以使用前台服务,Android 14 制定了严格的规则。
此外,在 Android 14 中,我们还引入了一个新 API,用于指定作业必须是用户发起的数据传输作业。此 API 适用于需要由用户发起的持续时间较长的数据传输,例如从远程服务器下载文件。这些类型的任务应使用由用户发起的数据传输作业。
由用户发起的数据传输作业由用户启动。这些作业需要一个通知,会立即启动,并且可能在系统条件允许的情况下长时间运行。您可以同时运行多个由用户发起的数据传输作业。
必须在应用对用户可见的情况下(或在某个允许的条件下)安排由用户发起的作业。满足所有限制条件后,操作系统可以执行由用户发起的作业,具体取决于系统运行状况限制。系统还可以根据提供的估算载荷大小来确定作业的执行时长。
用户发起的数据传输作业的权限
由用户发起的数据传输作业需要具备以下新权限才能运行:RUN_USER_INITIATED_JOBS
。系统会自动授予此权限。如果您未在应用清单中声明此权限,系统会抛出 SecurityException
。
安排用户发起的数据传输作业的流程
To run a user initiated job, do the following:
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 ofJobService
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() { ... }
Declare the
RUN_USER_INITIATED_JOBS
permission in your manifest:<manifest ...> <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" /> <application ...> ... </application> </manifest>
Call the new
setUserInitiated()
method when building aJobInfo
object. It is also recommended that you offer a payload size estimate by callingsetEstimatedNetworkBytes()
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()
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)
When the job is being executed, ensure you call
setNotification()
on theJobService
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. } }
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.When execution is complete, call
jobFinished()
to signal to the system that the job is complete, or that the job should be rescheduled.
可以停止用户发起的数据传输作业
用户和系统都可以停止用户发起的传输作业。
由用户从任务管理器提供
The user can stop a user-initiated data transfer job that appears in the Task Manager.
At the moment that the user presses Stop, the system does the following:
- Terminates your app's process immediately, including all other jobs or foreground services running.
- Doesn't call
onStopJob()
for any running jobs. - Prevets user-visible jobs from being rescheduled.
For these reasons, it's recommended to provide controls in the notification posted for the job to allow gracefully stopping and rescheduling the job.
Note that, under special circumstances, the Stop button doesn't appear next to the job in the Task Manager, or the job isn't shown in the Task Manager at all.
由系统提供
与常规作业不同,用户发起的数据传输作业不受应用待机模式存储分区配额的影响。但是,如果出现以下任一情况,系统仍会停止作业:
- 不再满足开发者定义的约束条件。
- 系统确定该作业的运行时间超出了完成数据传输任务所需的时间。
- 系统需要优先考虑系统运行状况,并因发热程度上升而停止作业。
- 应用进程因设备内存不足而被终止。
系统停止作业(并非因为内存不足)时,系统会调用 onStopJob()
,系统会在系统认为最佳的时间重试作业。检查您的应用是否可以保留数据传输状态(即使未调用 onStopJob()
),并且您的应用可以在再次调用 onStartJob()
时恢复此状态。
允许安排用户发起的数据传输作业的条件
只有当应用处于可见窗口中或满足特定条件时,应用才能启动用户发起的数据传输作业。为确定何时可以安排用户发起的数据传输作业,系统会采用允许应用在特殊情况下从后台启动 activity 的一组相同条件。值得注意的是,这组条件与后台启动的前台服务限制的豁免集不同。
上述说明的例外情况包括:
- 如果应用可以从后台启动 activity,则也可以从后台启动用户发起的数据传输作业。
- 如果应用在最近用过屏幕上现有任务的返回堆栈中有 activity,单靠这一点并不允许运行用户发起的数据传输作业。
如果作业安排在允许的条件列表中未列出的其他时间,则作业将失败并返回 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