Android WorkManager

谷歌在今年的Google I/O上宣布了一项非常令人兴奋的功能,该功能允许开发人员执行传统上需要详细了解各种API级别和可用于这些API的后台任务库的后台任务(简单点说就是”管理一些要在后台工作的任务, – 即使你的应用没启动也能保证任务能被执行”),这就是WorkManager,WorkManager提供了从其他API(例如JobSchedulerFirebase.JobDispatcherAlarmManagerServices)中获得的功能,而无需研究哪种API可用于您的设备或API

这句话是什么意思?既然能用JobSchedulerAlarmManager等,何必再出来个WorkManager呢?其实WorkManager在底层也是看你是什么版本来选到底是JobScheduler,AlamarManager来做。
JobScheduler是在SDK21中才有的,而且在SDK21中还有Bug,稳定可用是从SDK23开始的. 而AlarmManager一直存在,但是AlarmManager也不是最好的选择,
注意:如果您的应用程序的目标是API级别26或更高,则当应用程序本身不在前台时,系统会对运行后台服务施加限制。在大多数情况下,您的应用程序应该使用预定作业。所以WorkManager在底层, 会根据你的设备情况, 选用JobScheduler,FirebaseJobDispatcher,或是AlarmManager

WorkManager,它在应用被杀, 甚至设备重启后仍能保证你安排给他的任务能得到执行。那这样我们以后是不是可以把后台任务都用它来实现了?其实Google自己也说了:WorkManager并不是为了那种在应用内的后台线程而设计出来的. 这种需求你应该使用ThreadPool

具体的规则如下:

WorkManager根据以下标准在可用时使用对应的底层工作服务:

  • API 23+使用JobScheduler
  • API 14-22
  • 如果在应用中使用了Firebase JobDispatcher并且有可选的Firebase依赖项就使用Firebase JobDispatcher
  • 否则使用自定义的AlarmManager + BroadcastReceiver的实现方式

android系统使用crontab定时任务 android 定时任务 app_API

组成部分:

  • WorkManager:通过对应的参数来接受work并排队执行work
  • Worker:通过实现doWork()方法来指定在后台执行的具体功能。
  • WorkRequest:代表一个独立的任务。它会告诉你哪个worker加入了,以及它需要满足哪些约束才能运行。WorkRequest是一个抽象类,您将使用OneTimeWorkRequestPeriodicWorkRequest
  • WorkStatus:为每个WorkRequest对象提供数据。

好了下面开始演示:

首先加入依赖:

dependencies {
    def work_version = "1.0.0-alpha02"
    implementation "android.arch.work:work-runtime:$work_version"
}
  • 继承Worker类并实现doWork()方法
class MineWorker : Worker() {
    override fun doWork(): WorkerResult {
        Log.e("@@@", "dang dang dang !!!")
        return WorkerResult.SUCCESS
    }
}
  • 接下来如果想要该Worker执行,需要调用WorkManager将该Worker添加到队列中
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val workRequest = OneTimeWorkRequest.Builder(MineWorker::class.java).build()
        val instance = WorkManager.getInstance()
        instance.enqueue(workRequest)

    }
}

好了,执行下:

05-29 12:15:51.066 6360-6406/com.charon.workmanagerdemo E/@@@: dang dang dang !!!

并没什么卵用,因为这个示例代码就上来就执行一次。 很显然这个不合理啊,我想给该worker添加一些限制条件,例如我想在手机充电时,并且有网的情况下执行某个任务,
而且我想让它执行多次,这里可以通过Constraints来限制:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresCharging(true)
            .build()

    val workRequest = OneTimeWorkRequest.Builder(MineWorker::class.java)
            .setConstraints(constraints).build()

    val instance = WorkManager.getInstance()
    instance.enqueue(workRequest)
}

我们把手机网络关掉运行下,这次运行后并没有log打印。那现在把网络打开:

05-29 12:21:02.061 7092-7241/? E/@@@: dang dang dang !!!

当然我们可以获取每个请求的状态,以及取消该请求:

val statusById: LiveData<WorkStatus> = instance.getStatusById(workRequest.id)
instance.cancelWorkById(workRequest.id)

如果想要顺序的去执行不同的OneTimeWorker也是很方便的:

WorkContinuation chain1 = WorkManager.getInstance()
    .beginWith(workA)
    .then(workB);
WorkContinuation chain2 = WorkManager.getInstance()
    .beginWith(workC)
    .then(workD);
WorkContinuation chain3 = WorkContinuation
    .combine(chain1, chain2)
    .then(workE);
chain3.enqueue();

上面都是用了OneTimeWorkRequest,如果你想定期的去执行某一个worker的话,可以使用PeriodicWorkRequest:

val workRequest = PeriodicWorkRequest.Builder(MineWorker::class.java, 3, TimeUnit.SECONDS)
        .setConstraints(constraints)
        .setInputData(data)
        .build()

但是我使用这个定期任务执行的时候也只是执行了一次,并没有像上面的代码中那样3秒执行一次,什么鬼?
我们看看PeriodicWorkRequest的源码:

/**
 * A class that represents a request for repeating work.
 */

public final class PeriodicWorkRequest extends WorkRequest {

    /**
     * The minimum interval duration for {@link PeriodicWorkRequest}, in milliseconds.
     * Based on {@see https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/job/JobInfo.java#110}.
     */
    public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes.

上面说的很明白,最小间隔15分钟,- -!

总体来说,WorkManager并不是要取代线程池AsyncTask/RxJava.反而是像AlarmManager来做定时任务的意思.即保证你给它的任务能完成, 即使你的应用都没有被打开, 或是设备重启后也能让你的任务被执行.WorkManager在设计上设计得比较好.没有把worker,任务混为一谈,而是把它们解耦成Worker,WorkRequest.这样分层就清晰多了, 也好扩展.

到了这里突然有了一个大胆的想法。看到没有它能保证任务的执行。
我们之前写过一篇文章Android卸载反馈
里面用到了c中的fork来保证存活,达到常驻内存的功能,如果PeriodicWorkRequest的最小间隔时间比较短不是15分钟的话,那这里是不是也可以用WorkManager来实现? 好了,不说了。

参考: