Android 作业计划程序Android Job Scheduler

本指南讨论如何使用 Android 作业计划程序 API (在运行 Android 5.0 (API 级别21)及更高版本的 Android 设备上提供)来计划后台工作。This guide discusses how to schedule background work using the Android Job Scheduler API, which is available on Android devices running Android 5.0 (API level 21) and higher.

概述Overview

使 Android 应用程序对用户进行响应的最佳方式之一是确保在后台执行复杂的或长时间运行的工作。One of the best ways to keep an Android application responsive to the user is to ensure that complex or long running work is performed in the background. 但重要的是,后台工作不会对用户对设备的体验产生负面影响。However, it is important that background work will not negatively impact the user's experience with the device.

例如,后台作业可能每三或四分钟轮询一次网站,以查询特定数据集的更改。For example, a background job might poll a website every three or four minutes to query for changes to a particular dataset. 这似乎是良性的,但会对电池寿命产生灾难性影响。This seems benign, however it would have a disastrous impact on battery life. 应用程序将重复唤醒设备,将 CPU 提升到较高的电源状态,启动无线电收发器,使网络请求,然后处理结果。The application will repeatedly wake up the device, elevate the CPU to a higher power state, power up the radios, make the network requests, and then processing the results. 这种情况更糟,因为设备不会立即断电并返回到低功耗空闲状态。It gets worse because the device will not immediately power down and return to the low-power idle state. 计划不良的后台工作可能会意外地使设备处于一种不必要和过多电源要求的状态。Poorly scheduled background work may inadvertently keep the device in a state with unnecessary and excessive power requirements. 此看似合法的活动(轮询网站)会使设备在相对较短的时间内不可用。This seemingly innocent activity (polling a website) will render the device unusable in a relatively short period of time.

Android 提供以下 Api,可帮助在后台执行工作,但其本身并不是用于智能作业计划的。Android provides the following APIs to help with performing work in the background but by themselves they are not sufficient for intelligent job scheduling.

  • 意向服务 – 意向服务非常适合用于执行工作,不过,它们无法计划工作。Intent Services – Intent Services are great for performing the work, however they provide no way to schedule work.
  • AlarmManager – 这些 api 只允许计划工作,但不能实际执行工作。AlarmManager – These APIs only allow work to be scheduled but provide no way to actually perform the work. 此外,AlarmManager 只允许基于时间的约束,这意味着在特定时间或经过一段时间后发出警报。Also, the AlarmManager only allows time based constraints, which means raise an alarm at a certain time or after a certain period of time has elapsed.
  • Android 应用 – 的 广播接收 方可以设置广播接收器,以便在响应系统范围内的事件或意向时执行工作。Broadcast Receivers – An Android app can setup broadcast receivers to perform work in response to system-wide events or Intents. 但是,广播接收器不提供对作业运行时间的任何控制。However, broadcast receivers don't provide any control over when the job should be run. 同时,Android 操作系统中的更改将限制广播接收方的工作时间或它们可以响应的工作类型。Also changes in the Android operating system will restrict when broadcast receivers will work, or the kinds of work that they can respond to.

有效地执行后台工作(有时称为_后台作业_或_作业_)有两个主要功能:There are two key features to efficiently performing background work (sometimes referred to as a background job or a job):

  1. 智能地计划工作– 重要的是,当应用程序在后台执行工作时,这一点非常重要。Intelligently scheduling the work – It is important that when an application is doing work in the background that it does so as a good citizen. 理想情况下,应用程序不应要求运行作业。Ideally, the application should not demand that a job be run. 相反,应用程序应指定在作业可以运行时必须满足的条件,然后使用满足条件时将执行工作的操作系统计划该作业。Instead, the application should specify conditions that must be met for when the job can run, and then schedule that job with the operating system that will perform the work when the conditions are met. 这允许 Android 运行作业,以确保设备上的最高效率。This allows Android to run the job to ensure maximum efficiency on the device. 例如,可能会批处理网络请求同时运行全部,以充分利用网络所涉及的开销。For example, network requests may be batched to run all at the same time to make maximum use of overhead involved with networking.
  2. 封装工作– 用于执行后台工作的代码应封装在独立于用户界面的离散组件中,如果无法完成某些任务的工作,则可以相对容易地重新安排这些工作在于.Encapsulating the work – The code to perform the background work should be encapsulated in a discrete component that can be run independently of the user interface and will be relatively easy to reschedule if the work fails to complete for some reason.

Android 作业计划程序是一种内置于 Android 操作系统的框架,可提供 Fluent API 来简化计划后台工作。The Android Job Scheduler is a framework built in to the Android operating system that provides a fluent API to simplify scheduling background work. Android 作业计划程序包含以下类型:The Android Job Scheduler consists of the following types:

  • Android.App.Job.JobScheduler 是一种系统服务,用于计划、执行和代表 Android 应用程序取消作业。The Android.App.Job.JobScheduler is a system service that is used to schedule, execute, and if necessary cancel, jobs on behalf of an Android application.
  • Android.App.Job.JobService 是必须使用将在应用程序的主线程上运行作业的逻辑进行扩展的抽象类。An Android.App.Job.JobService is an abstract class that must be extended with the logic that will run the job on the main thread of the application. 这意味着 JobService 负责以异步方式执行工作。This means that the JobService is responsible for how the work is to be performed asynchronously.
  • Android.App.Job.JobInfo 对象包含在作业应运行时指导 Android 的条件。An Android.App.Job.JobInfo object holds the criteria to guide Android when the job should run.

若要计划使用 Android 作业计划程序,Xamarin Android 应用程序必须将扩展 JobService 类的类中的代码封装。To schedule work with the Android Job Scheduler, a Xamarin.Android application must encapsulate the code in a class that extends the JobService class. JobService 具有三种可在作业的生存期内调用的生命周期方法:JobService has three lifecycle methods that can be called during the lifetime of the job:

  • Bool OnStartJob (JobParameters parameters) – 此方法由 JobScheduler 调用以执行工作,并在应用程序的主线程上运行。bool OnStartJob(JobParameters parameters) – This method is called by the JobScheduler to perform work, and runs on the main thread of the application. JobService 需要以异步方式执行工作,并在剩余工作时 true,或在完成工作时 falseIt is the responsibility of the JobService to asynchronously perform the work and true if there is work remaining, or false if the work is done.

    JobScheduler 调用此方法时,它将在作业期间请求并保留 Android 中的 wakelock。When the JobScheduler calls this method, it will request and retain a wakelock from Android for the duration of the job. 当作业完成时,JobService 通过调用 JobFinished 方法(如下所述)来告知此事实的 JobSchedulerWhen the job is finished, it is the responsibility of the JobService to tell the JobScheduler of this fact by call the JobFinished method (described next).

  • JobFinished (JobParameters parameters,Bool needsReschedule) – 必须由 JobService 调用此方法,以通知 JobScheduler 工作完成。JobFinished(JobParameters parameters, bool needsReschedule) – This method must be called by the JobService to tell the JobScheduler that the work is done. 如果未调用 JobFinishedJobScheduler 将不会删除 wakelock,从而导致不必要的电池排出。If JobFinished is not called, the JobScheduler will not remove the wakelock, causing unnecessary battery drain.

  • Bool OnStopJob (JobParameters parameters) – 在 Android 提前停止作业时调用此。bool OnStopJob(JobParameters parameters) – This is called when the job is prematurely stopped by Android. 如果应根据重试条件重新安排作业(更详细地讨论),则应返回 trueIt should return true if the job should be rescheduled based on the retry criteria (discussed below in more detail).

可以指定_约束_或_触发器_,以便控制作业的运行时间和运行时间。It is possible to specify constraints or triggers that will control when a job can or should run. 例如,可以限制作业,使其仅在设备充电时运行,或在拍摄照片时启动作业。For example, it is possible to constrain a job so that it will only run when the device is charging or to start a job when a picture is taken.

本指南将详细讨论如何实现 JobService 类并使用 JobScheduler进行安排。This guide will discuss in detail how to implement a JobService class and schedule it with the JobScheduler.

要求Requirements

Android 作业计划程序需要 Android API 级别21(Android 5.0)或更高版本。The Android Job Scheduler requires Android API level 21 (Android 5.0) or higher.

使用 Android 作业计划程序Using the Android Job Scheduler

使用 Android JobScheduler API 有三个步骤:There are three steps for using the Android JobScheduler API:

  1. 实现 JobService 类型以封装工作。Implement a JobService type to encapsulate the work.
  2. 使用 JobInfo.Builder 对象创建 JobInfo 对象,该对象将包含用于运行作业的 JobScheduler 的条件。Use a JobInfo.Builder object to create the JobInfo object that will hold the criteria for the JobScheduler to run the job.
  3. 使用 JobScheduler.Schedule安排作业。Schedule the job using JobScheduler.Schedule.

实现 JobServiceImplement a JobService

Android 作业计划程序库执行的所有工作都必须在一个扩展 Android.App.Job.JobService 抽象类的类型中完成。All work performed by the Android Job Scheduler library must be done in a type that extends the Android.App.Job.JobService abstract class. 创建 JobService 非常类似于使用 Android framework 创建 ServiceCreating a JobService is very similar to creating a Service with the Android framework:

  1. 扩展 JobService 类。Extend the JobService class.
  2. 使用 ServiceAttribute 修饰子类,并将 Name 参数设置为一个字符串,该字符串由包名称和类名组成(请参见以下示例)。Decorate the subclass with the ServiceAttribute and set the Name parameter to a string that is made up of the package name and the name of the class (see the following example).
  3. ServiceAttribute 上的 Permission 属性设置为字符串 android.permission.BIND_JOB_SERVICESet the Permission property on the ServiceAttribute to the string android.permission.BIND_JOB_SERVICE.
  4. 重写 OnStartJob 方法,并添加用于执行工作的代码。Override the OnStartJob method, adding the code to perform the work. Android 将在应用程序的主线程上调用此方法以运行作业。Android will invoke this method on the main thread of the application to run the job. 需要在线程上执行几毫秒时间,以避免阻塞应用程序的工作。Work that will take longer that a few milliseconds should be performed on a thread to avoid blocking the application.
  5. 完成工作后,JobService 必须调用 JobFinished 方法。When the work is done, the JobService must call the JobFinished method. 此方法是 JobService 告诉 JobScheduler 完成工作的方式。This method is how JobService tells the JobScheduler that work is done. 未能调用 JobFinished 将导致 JobService 在设备上施加不必要的需求,缩短电池寿命。Failure to call JobFinished will result in the JobService putting unnecessary demands on the device, shortening the battery life.
  6. 最好还是重写 OnStopJob 方法。It is a good idea to also override the OnStopJob method. 此方法由 Android 在作业完成之前关闭,并为 JobService 提供了正确释放任何资源的机会。This method is called by Android when the job is being shut down before it is finished and provides the JobService with an opportunity to properly dispose of any resources. 如果需要重新计划作业,则此方法应返回 true,如果不 desireable 重新运行作业,则应 falseThis method should return true if it is necessary to reschedule the job, or false if it is not desireable to re-run the job.

下面的代码示例是应用程序的最简单的 JobService,使用 TPL 异步执行一些工作:The following code is an example of the simplest JobService for an application, using the TPL to asynchronously perform some work:

[Service(Name = "com.xamarin.samples.downloadscheduler.DownloadJob", 
         Permission = "android.permission.BIND_JOB_SERVICE")]
public class DownloadJob : JobService
{
    public override bool OnStartJob(JobParameters jobParams)
    {            
        Task.Run(() =>
        {
            // Work is happening asynchronously
                      
            // Have to tell the JobScheduler the work is done. 
            JobFinished(jobParams, false);
        });

        // Return true because of the asynchronous work
        return true;  
    }

    public override bool OnStopJob(JobParameters jobParams)
    {
        // we don't want to reschedule the job if it is stopped or cancelled.
        return false; 
    }
}

创建 JobInfo 以计划作业Creating a JobInfo to schedule a job

Xamarin Android 应用程序不直接实例化 JobService,而是将 JobInfo 对象传递到 JobSchedulerXamarin.Android applications do not instantiate a JobService directly, instead they will pass a JobInfo object to the JobScheduler. JobScheduler 将实例化请求的 JobService 对象,根据 JobInfo中的元数据来计划和运行 JobServiceThe JobScheduler will instantiate the requested JobService object, scheduling and running the JobService according to the metadata in the JobInfo. JobInfo 对象必须包含以下信息:A JobInfo object must contain the following information:

  • JobId – 这是用于标识 JobScheduler作业的 int 值。JobId – this is an int value that is used to identify a job to the JobScheduler. 重复使用此值将更新所有现有作业。Reusing this value will update any existing jobs. 对于应用程序,该值必须是唯一的。The value must be unique for the application.
  • JobService – 此参数是显式标识 JobScheduler 用于运行作业的类型的 ComponentNameJobService – this parameter is a ComponentName that explicitly identifies the type that the JobScheduler should use to run a job.

此扩展方法演示如何使用 Android Context创建 JobInfo.Builder,例如活动:This extension method demonstrates how to create a JobInfo.Builder with an Android Context, such as an Activity:

public static class JobSchedulerHelpers
{
    public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId) where T:JobService
    {
        var javaClass = Java.Lang.Class.FromType(typeof(T));
        var componentName = new ComponentName(context, javaClass);
        return new JobInfo.Builder(jobId, componentName);
    }
}

// Sample usage - creates a JobBuilder for a DownloadJob and sets the Job ID to 1.
var jobBuilder = this.CreateJobBuilderUsingJobId<DownloadJob>(1);

var jobInfo = jobBuilder.Build();  // creates a JobInfo object.

Android 作业计划程序的一项强大功能是能够控制作业的运行时间或在什么条件下运行作业。A powerful feature of the Android Job Scheduler is the ability to control when a job runs or under what conditions a job may run. 下表介绍 JobInfo.Builder 上的一些方法,这些方法允许应用程序在作业运行时影响:The following table describes some of the methods on JobInfo.Builder that allow an app to influence when a job can run:

方法Method 说明Description
SetMinimumLatency 指定在运行作业之前应观察的延迟(以毫秒为单位)。Specifies that a delay (in milliseconds) that should be observed before a job is run.
SetOverridingDeadline 声明作业在此时间之前必须运行(以毫秒为单位)。Declares the that the job must run before this time (in milliseconds) has elapsed.
SetRequiredNetworkType 指定作业的网络要求。Specifies the network requirements for a job.
SetRequiresBatteryNotLow 仅当设备不向用户显示 "电池电量不足" 警告时,作业才能运行。The job may only run when the device is not displaying a "low battery" warning to the user.
SetRequiresCharging 作业只能在电池充电时运行。The job may only run when the battery is charging.
SetDeviceIdle 当设备忙时将运行该作业。The job will run when the device is busy.
SetPeriodic 指定应定期运行作业。Specifies that the job should be regularly run.
SetPersisted 作业应跨设备重新启动 perisist。The job should perisist across device reboots.

SetBackoffCriteria 提供了有关 JobScheduler 在尝试再次运行作业之前应等待的时间的一些指导。The SetBackoffCriteria provides some guidance on how long the JobScheduler should wait before trying to run a job again. 回退条件有两个部分:延迟(以毫秒为单位,默认值为30秒)和应使用的后端类型(有时称为 "回退策略" 或 "重试策略")。There are two parts to the backoff criteria: a delay in milliseconds (default value of 30 seconds)and type of back off that should be used (sometimes referred to as the backoff policy or the retry policy). 这两个策略封装在 Android.App.Job.BackoffPolicy 枚举中:The two policies are encapsulated in the Android.App.Job.BackoffPolicy enum:

  • BackoffPolicy.Exponential – 指数回退策略会在每次发生故障后以指数方式增加初始回退值。BackoffPolicy.Exponential – An exponential backoff policy will increase the initial backoff value exponentially after each failure. 第一次作业失败时,库将等待在重新计划作业之前指定的初始间隔–示例30秒。The first time a job fails, the library will wait the initial interval that is specified before rescheduling the job – example 30 seconds. 第二次作业失败时,库将等待至少60秒,然后再尝试运行作业。The second time the job fails, the library will wait at least 60 seconds before trying to run the job. 第三次尝试失败后,库将等待120秒,依此类推。After the third failed attempt, the library will wait 120 seconds, and so on. 这是默认值。This is the default value.
  • BackoffPolicy.Linear – 此策略是一种线性回退,应将作业重新安排为按设置的时间间隔运行(直到成功)。BackoffPolicy.Linear – This strategy is a linear backoff that the job should be rescheduled to run at set intervals (until it succeeds). 线性回退最适用于必须尽快完成的工作,或者用于快速解决自身问题的问题。Linear backoff is best suited for work that must be completed as soon as possible or for problems that will quickly resolve themselves.

有关创建 JobInfo 对象的更多详细信息,请参阅Google 的有关 JobInfo.Builder 类的文档For more details on create a JobInfo object, please read Google's documentation for the JobInfo.Builder class.

通过 JobInfo 将参数传递给作业Passing parameters to a job via the JobInfo

通过创建与 Job.Builder.SetExtras 方法一起传递的 PersistableBundle,将参数传递给作业:Parameters are passed to a job by creating a PersistableBundle that is passed along with the Job.Builder.SetExtras method:

var jobParameters = new PersistableBundle();
jobParameters.PutInt("LoopCount", 11);

var jobBuilder = this.CreateJobBuilderUsingJobId<DownloadJob>(1)
                     .SetExtras(jobParameters)
                     .Build();

PersistableBundle 是通过 JobServiceOnStartJob 方法中的 Android.App.Job.JobParameters.Extras 属性访问的:The PersistableBundle is accessed from the Android.App.Job.JobParameters.Extras property in the OnStartJob method of a JobService:

public override bool OnStartJob(JobParameters jobParameters)
{
    var loopCount = jobParams.Extras.GetInt("LoopCount", 10);
    
    // rest of code omitted
} 

计划作业Scheduling a job

若要计划作业,Xamarin Android 应用程序将获取对 JobScheduler 系统服务的引用,并使用上一步中创建的 JobInfo 对象调用 JobScheduler.Schedule 方法。To schedule a job, a Xamarin.Android application will get a reference to the JobScheduler system service and call the JobScheduler.Schedule method with the JobInfo object that was created in the previous step. JobScheduler.Schedule 将立即返回以下两个整数值之一:JobScheduler.Schedule will immediately return with one of two integer values:

  • JobScheduler. ResultSuccess – 已成功安排作业。JobScheduler.ResultSuccess – The job has been successfully scheduled.
  • JobScheduler. ResultFailure – 无法计划作业。JobScheduler.ResultFailure – The job could not be scheduled. 这通常是由 JobInfo 参数冲突引起的。This is typically caused by conflicting JobInfo parameters.

此代码是计划作业并向用户通知计划尝试结果的示例:This code is an example of scheduling a job and notifying the user of the results of the scheduling attempt:

var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
var scheduleResult = jobScheduler.Schedule(jobInfo);

if (JobScheduler.ResultSuccess == scheduleResult)
{
    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_success, Snackbar.LengthShort);
    snackBar.Show();
}
else
{
    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_failure, Snackbar.LengthShort);
    snackBar.Show();
}

取消作业Cancelling a job

可以取消已计划的所有作业,或者只是使用 JobsScheduler.CancelAll() 方法或 JobScheduler.Cancel(jobId) 方法的单个作业:It is possible to cancel all the jobs that have been scheduled, or just a single job using the JobsScheduler.CancelAll() method or the JobScheduler.Cancel(jobId) method:

// Cancel all jobs
jobScheduler.CancelAll(); 

// to cancel a job with jobID = 1
jobScheduler.Cancel(1)

总结Summary

本指南讨论了如何使用 Android 作业计划程序在后台智能地执行工作。This guide discussed how to use the Android Job Scheduler to intelligently perform work in the background. 本指南讨论了如何封装要作为 JobService 执行的工作,以及如何使用 JobScheduler 来计划工作、使用 JobTrigger 指定条件以及如何使用 RetryStrategy处理失败。It discussed how to encapsulate the work to be performed as a JobService and how to use the JobScheduler to schedule that work, specifying the criteria with a JobTrigger and how failures should be handled with a RetryStrategy.