|
| 1 | +Android WorkManager |
| 2 | +=== |
| 3 | + |
| 4 | +谷歌在今年的`Google I/O`上宣布了一项非常令人兴奋的功能,该功能允许开发人员执行传统上需要详细了解各种API级别和可用于这些API的后台任务库的后台任务(简单点说就是”管理一些要在后台工作的任务, – 即使你的应用没启动也能保证任务能被执行”),这就是[WorkManager](https://developer.android.com/reference/androidx/work/WorkManager),`WorkManager`提供了从其他`API`(例如`JobScheduler`,`Firebase.JobDispatcher`,`AlarmManager`和`Services`)中获得的功能,而无需研究哪种`API`可用于您的设备或`API`。 |
| 5 | + |
| 6 | +这句话是什么意思?既然能用`JobScheduler`和`AlarmManager`等,何必再出来个`WorkManager`呢?其实`WorkManager`在底层也是看你是什么版本来选到底是`JobScheduler`,`AlamarManager`来做。 |
| 7 | +`JobScheduler`是在`SDK`21中才有的,而且在`SDK`21中还有`Bug`,稳定可用是从`SDK`23开始的. 而`AlarmManager`一直存在,但是`AlarmManager`也不是最好的选择, |
| 8 | +注意:如果您的应用程序的目标是`API`级别26或更高,则当应用程序本身不在前台时,系统会对运行后台服务施加限制。在大多数情况下,您的应用程序应该使用预定作业。所以`WorkManager`在底层, 会根据你的设备情况, 选用`JobScheduler`,`Firebase`的`JobDispatcher`,或是`AlarmManager`。 |
| 9 | + |
| 10 | + |
| 11 | +`WorkManager`,它在应用被杀, 甚至设备重启后仍能保证你安排给他的任务能得到执行。那这样我们以后是不是可以把后台任务都用它来实现了?其实`Google`自己也说了:`WorkManager`并不是为了那种在应用内的后台线程而设计出来的. 这种需求你应该使用`ThreadPool`。 |
| 12 | + |
| 13 | +具体的规则如下: |
| 14 | + |
| 15 | +`WorkManager`根据以下标准在可用时使用对应的底层工作服务: |
| 16 | + |
| 17 | +- 在`API 23+`使用`JobScheduler` |
| 18 | +- 在`API 14-22`中 |
| 19 | + - 如果在应用中使用了`Firebase JobDispatcher`并且有可选的`Firebase`依赖项就使用`Firebase JobDispatcher` |
| 20 | + - 否则使用自定义的`AlarmManager + BroadcastReceiver`的实现方式 |
| 21 | + |
| 22 | + |
| 23 | +组成部分: |
| 24 | + |
| 25 | +- `WorkManager`:通过对应的参数来接受`work`并排队执行`work`。 |
| 26 | +- `Worker`:通过实现`doWork()`方法来指定在后台执行的具体功能。 |
| 27 | +- `WorkRequest`:代表一个独立的任务。它会告诉你哪个`worker`加入了,以及它需要满足哪些约束才能运行。`WorkRequest`是一个抽象类,您将使用[OneTimeWorkRequest](https://developer.android.com/reference/androidx/work/OneTimeWorkRequest)或[PeriodicWorkRequest](https://developer.android.com/reference/androidx/work/PeriodicWorkRequest)。 |
| 28 | +- `WorkStatus`:为每个WorkRequest对象提供数据。 |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +好了下面开始演示: |
| 33 | + |
| 34 | +首先加入依赖: |
| 35 | + |
| 36 | +``` |
| 37 | +dependencies { |
| 38 | + def work_version = "1.0.0-alpha02" |
| 39 | + implementation "android.arch.work:work-runtime:$work_version" |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +- 继承`Worker`类并实现`doWork()`方法 |
| 44 | + |
| 45 | +```kotlin |
| 46 | +class MineWorker : Worker() { |
| 47 | + override fun doWork(): WorkerResult { |
| 48 | + Log.e("@@@", "dang dang dang !!!") |
| 49 | + return WorkerResult.SUCCESS |
| 50 | + } |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +- 接下来如果想要该`Worker`执行,需要调用`WorkManager`将该`Worker`添加到队列中 |
| 55 | + |
| 56 | +```kotlin |
| 57 | +class MainActivity : AppCompatActivity() { |
| 58 | + |
| 59 | + override fun onCreate(savedInstanceState: Bundle?) { |
| 60 | + super.onCreate(savedInstanceState) |
| 61 | + setContentView(R.layout.activity_main) |
| 62 | + |
| 63 | + val workRequest = OneTimeWorkRequest.Builder(MineWorker::class.java).build() |
| 64 | + val instance = WorkManager.getInstance() |
| 65 | + instance.enqueue(workRequest) |
| 66 | + |
| 67 | + } |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +好了,执行下: |
| 72 | +``` |
| 73 | +05-29 12:15:51.066 6360-6406/com.charon.workmanagerdemo E/@@@: dang dang dang !!! |
| 74 | +``` |
| 75 | + |
| 76 | +并没什么卵用,因为这个示例代码就上来就执行一次。 很显然这个不合理啊,我想给该`worker`添加一些限制条件,例如我想在手机充电时,并且有网的情况下执行某个任务, |
| 77 | +而且我想让它执行多次,这里可以通过`Constraints`来限制: |
| 78 | + |
| 79 | +```kotlin |
| 80 | +override fun onCreate(savedInstanceState: Bundle?) { |
| 81 | + super.onCreate(savedInstanceState) |
| 82 | + setContentView(R.layout.activity_main) |
| 83 | + |
| 84 | + val constraints = Constraints.Builder() |
| 85 | + .setRequiredNetworkType(NetworkType.CONNECTED) |
| 86 | + .setRequiresCharging(true) |
| 87 | + .build() |
| 88 | + |
| 89 | + val workRequest = OneTimeWorkRequest.Builder(MineWorker::class.java) |
| 90 | + .setConstraints(constraints).build() |
| 91 | + |
| 92 | + val instance = WorkManager.getInstance() |
| 93 | + instance.enqueue(workRequest) |
| 94 | +} |
| 95 | +``` |
| 96 | +我们把手机网络关掉运行下,这次运行后并没有`log`打印。那现在把网络打开: |
| 97 | + |
| 98 | +``` |
| 99 | +05-29 12:21:02.061 7092-7241/? E/@@@: dang dang dang !!! |
| 100 | +``` |
| 101 | + |
| 102 | + |
| 103 | +当然我们可以获取每个请求的状态,以及取消该请求: |
| 104 | +```kotlin |
| 105 | +val statusById: LiveData<WorkStatus> = instance.getStatusById(workRequest.id) |
| 106 | +instance.cancelWorkById(workRequest.id) |
| 107 | +``` |
| 108 | +如果想要顺序的去执行不同的`OneTimeWorker`也是很方便的: |
| 109 | +```kotlin |
| 110 | +WorkContinuation chain1 = WorkManager.getInstance() |
| 111 | + .beginWith(workA) |
| 112 | + .then(workB); |
| 113 | +WorkContinuation chain2 = WorkManager.getInstance() |
| 114 | + .beginWith(workC) |
| 115 | + .then(workD); |
| 116 | +WorkContinuation chain3 = WorkContinuation |
| 117 | + .combine(chain1, chain2) |
| 118 | + .then(workE); |
| 119 | +chain3.enqueue(); |
| 120 | +``` |
| 121 | + |
| 122 | +上面都是用了`OneTimeWorkRequest`,如果你想定期的去执行某一个`worker`的话,可以使用`PeriodicWorkRequest`: |
| 123 | +```kotlin |
| 124 | +val workRequest = PeriodicWorkRequest.Builder(MineWorker::class.java, 3, TimeUnit.SECONDS) |
| 125 | + .setConstraints(constraints) |
| 126 | + .setInputData(data) |
| 127 | + .build() |
| 128 | +``` |
| 129 | + |
| 130 | +但是我使用这个定期任务执行的时候也只是执行了一次,并没有像上面的代码中那样3秒执行一次,什么鬼? |
| 131 | +我们看看`PeriodicWorkRequest`的源码: |
| 132 | + |
| 133 | +```kotlin |
| 134 | +/** |
| 135 | + * A class that represents a request for repeating work. |
| 136 | + */ |
| 137 | + |
| 138 | +public final class PeriodicWorkRequest extends WorkRequest { |
| 139 | + |
| 140 | + /** |
| 141 | + * The minimum interval duration for {@link PeriodicWorkRequest}, in milliseconds. |
| 142 | + * Based on {@see https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/job/JobInfo.java#110}. |
| 143 | + */ |
| 144 | + public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes. |
| 145 | +``` |
| 146 | + |
| 147 | +上面说的很明白,最小间隔15分钟,- -! |
| 148 | + |
| 149 | + |
| 150 | +总体来说,`WorkManager`并不是要取代线程池`AsyncTask/RxJava`.反而是像`AlarmManager`来做定时任务的意思.即保证你给它的任务能完成, 即使你的应用都没有被打开, 或是设备重启后也能让你的任务被执行.`WorkManager`在设计上设计得比较好.没有把`worker`,任务混为一谈,而是把它们解耦成`Worker`,`WorkRequest`.这样分层就清晰多了, 也好扩展. |
| 151 | + |
| 152 | + |
| 153 | +到了这里突然有了一个大胆的想法。看到没有它能保证任务的执行。 |
| 154 | +我们之前写过一篇文章[Android卸载反馈](https://github.com/CharonChui/AndroidNote/blob/master/AdavancedPart/Android%E5%8D%B8%E8%BD%BD%E5%8F%8D%E9%A6%88.md) |
| 155 | +里面用到了`c`中的`fork`来保证存活,达到常驻内存的功能,如果`PeriodicWorkRequest`的最小间隔时间比较短不是15分钟的话,那这里是不是也可以用`WorkManager`来实现? 好了,不说了。 |
| 156 | + |
| 157 | + |
| 158 | + |
| 159 | +参考: |
| 160 | + |
| 161 | +- [官方文档](https://developer.android.com/topic/libraries/architecture/workmanager) |
| 162 | +- [Codelabs android-workmanager](https://codelabs.developers.google.com/codelabs/android-workmanager/#0) |
| 163 | +- [示例代码](https://github.com/googlecodelabs/android-workmanager) |
| 164 | + |
| 165 | +--- |
| 166 | + |
| 167 | +- 邮箱 :charon.chui@gmail.com |
| 168 | +- Good Luck! |
0 commit comments