(本篇文章已授权微信公众号guolin_blog(郭霖)独家发布
Workmanager简介
WorkManager
是用于使可延期工作入队的库,根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager
在应用程序运行时执行您的任务之一,WorkManager
可以在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行,WorkManager
会选择一种合适的方式来安排后台任务 - 具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobScheduler
,Firebase JobDispatcher
或AlarmManager
该库可保证在Constraints
满足要求后的某个时间执行。WorkManager
允许观察工作状态以及创建复杂工作链的能力。
WorkManager
支持两种类型的工作:OneTimeWorkRequest
和 PeriodicWorkRequest
。您可以使用WorkManager
排队请求,如下所示:
基本相关类(不全面):
-
Worker
:extend
抽象的Worker
类,在实现的方法执行逻辑操作 -
WorkRequest.Builder
:用于构建WorkRequest对象; -
WorkRequest
:单一任务的请求 系统已经实现了两个可以使用的子类
-
OneTimeWorkRequest
:只执行一次工作。 -
PeriodicWorkRequest
:重复性执行工作。这项工作执行多次,直到取消。下一个执行期间会发生间隔;
请注意, 执行可能会被推迟,因为受到Constraints
下设置的电池优化,, 息屏,网络 等模式影响。
-
Constraints
: 默认情况下,WorkRequests
可以立即运行。通过添加Constraints
可以对指定对任务运行的状态下进行约束(比如没网络,息屏,电池优化等);使用Constraints.Builder
构建Constraints
对象 ,并传递给WorkRequest.Builder
-
Data
: 用于构建一组持续化可供键值对。
WorkManager 使用
添加依赖:
//增加workManager管理后台任务
implementation "android.arch.work:work-runtime:1.0.1"
implementation "android.arch.work:work-firebase:1.0.0-alpha11"
使用方式
-
简单使用方式
1 实现Worker :
自定义MainWorker
,重写doWork
,并返回结果
class MainWorker(context: Context,workerParameters: WorkerParameters) :Worker(context,workerParameters){
override fun doWork(): Result {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
2 调用
由OneTimeWorkRequest.Builder().build()
创建对象WorkRequest
,然后将任务放入的WorkManager
管理的WorkRequest
队列(本次文章中都以OneTimeWorkRequest来写例子 )
val baseWork = OneTimeWorkRequest.Builder(MainWorker::class.java).build()
WorkManager.getInstance().enqueue(baseWork)
这样子简单的使用方式就可以了~~~~
但是如果你要监听数据信息然后获取数据进行逻辑操作呢(数据交互)?
可以看一下下面的使用···:
-
使用方式2 :监听数据方式
1实现work
这里面inputData.getString("LiveDataTest")
是从调用的时候传入进来的数据,ruturn
的时候使用Result.success(data)
'data'的数据类型的Data
返回的数据可以再output
中的键值对获取到
class BtnTwoWorker(context: Context, workerParameters: WorkerParameters) :Worker(context,workerParameters){
override fun doWork(): Result {
Log.i("MainWorker","点击${inputData.getString("LiveDataTest")}")
val data = Data.Builder().putString("LiveDataTest","LiveDataTest修改后的信息---").build()
return Result.success(data)
}
}
2 使用
代码中的第一步构建的就是 步骤一中的LiveDataTest
的数据,然后构建work
设置setInputData(data )
,通过WorkManager.getInstance().getWorkInfoByIdLiveData(liveDataWork.id)
获取LiveData<WorkInfo>
并进行监听数据,在监听数据中可以获取到outputdata
的键值对进行逻辑处理。
最后将构建的liveDataWork
放入WorkManager
中的队列等待运行 WorkManager.getInstance().enqueue(liveDataWork)
//1 构建 Data数据
var data = Data.Builder().putString("LiveDataTest","有生命的数据监听··").build()
//2 构建work
val liveDataWork = OneTimeWorkRequest.Builder(BtnTwoWorker::class.java).setInputData(data).build()
//3 通过work的id获取Status
val status = WorkManager.getInstance().getWorkInfoByIdLiveData(liveDataWork.id)
//4 监听状态以及数据的变化
//注意一点:这里的监听的时候 状态改变Observer就会跑一遍~~
status.observe(this, Observer {
if(it.state == WorkInfo.State.SUCCEEDED){
mViewModel.data.value = it.outputData.getString("LiveDataTest")
Log.i("WorkerActivity", it.outputData.getString("LiveDataTest")?:"空")
}
Log.i("WorkerActivity",it.state.name)
Log.i("WorkerActivity","${it.state.isFinished}")
})
//再放入到WorkManager的队列中
WorkManager.getInstance().enqueue(liveDataWork)
/////////////////////////////////////////////////////
2019-10-14 16:54:33.951 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: ENQUEUED
2019-10-14 16:54:33.951 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: false
2019-10-14 16:54:33.972 15770-15849/testview.zhen.com.myapplication I/MainWorker: 点击有生命的数据监听··
2019-10-14 16:54:33.975 15770-15800/testview.zhen.com.myapplication I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=fa81ea63-6c23-4d40-a72f-7f0eea3baf91, tags={ testview.zhen.com.myapplication.jetpack.BtnTwoWorker } ]
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: LiveDataTest修改后的信息---
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: SUCCEEDED
2019-10-14 16:54:34.000 15770-15770/testview.zhen.com.myapplication I/WorkerActivity: true
从上面执行过程中看出,回调了Work的两个运行状态RUNNING、SUCCESSESD
至于it.state.isFinished
这里面的实现为:
/**
* Returns {@code true} if this State is considered finished.
*
* @return {@code true} for {@link #SUCCEEDED}, {@link #FAILED}, and * {@link #CANCELLED}
* states
*/
public boolean isFinished() {
return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
}
当任务 成功/失败/取消
都是结束~
-
增加任务约束的使用
1 使用Constraints.Builder()创建并配置Constraints对象
里面的设置信息在API中皆有体现
setRequiresBatteryNotLow
:是否为低电量时运行setRequiredNetworkType
:网络连接设置setRequiresCharging
:是否在充电情况下运行setRequiresDeviceIdle
:设备是否为空闲setRequiresStorageNotLow
:设备可用存储是否不低于临界阈值
以上方法默认值都是false
-
使用方式3 :增加情况限制
Constraints
class DelayWorker(context: Context, workerParameters: WorkerParameters) :Worker(context,workerParameters){
override fun doWork(): Result {
Log.i("MainWorker","点击${inputData.getString("delayWork")} --所在线程 ${Thread.currentThread().name}")
val time = inputData.getString("time")
val data = Data.Builder().putString("LiveDataTest","LiveDataTest修改后的信息---").build()
return Result.success(data)
}
}
//1 增加约束条件
val constrains = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
//2 构建 Data数据
var data = Data.Builder().putString("delayWork","延时操作~~~").putInt("time",10).build()
//3 构建workRequest
val workConstrains = OneTimeWorkRequest.Builder(DelayWorker::class.java)
.setConstraints(constrains)
.setInputData(data)
.build()
//4 获取Status监听数据
val status = WorkManager.getInstance().getWorkInfoByIdLiveData(workConstrains.id)
status.observe(this, Observer {
Log.i("WorkerActivity",it?.state?.name)
if (it?.state!!.isFinished) {
Log.e("TestWorker", "Finish")
}
})
//5 添加到equque
WorkManager.getInstance().enqueue(workConstrains)
////////////////////////////////////////////////////////////////////////////////////
2019-10-14 17:57:28.140 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: ENQUEUED 【------没有连接网络的时候先等待】
2019-10-14 17:57:36.609 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: RUNNING 【------有网络的时候执行】
2019-10-14 17:57:36.610 25296-25469/testview.zhen.com.myapplication I/MainWorker: 点击延时操作~~~ --所在线程 pool-1-thread-1
2019-10-14 17:57:36.613 25296-25325/testview.zhen.com.myapplication I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=fc71f417-8289-4f8c-a489-4d8699960f3d, tags={ testview.zhen.com.myapplication.jetpack.DelayWorker } ]
2019-10-14 17:57:36.637 25296-25296/testview.zhen.com.myapplication I/WorkerActivity: SUCCEEDED
其它一些使用
-
取消任务
通过获取WorkRequest
的ID
或者TAG
获取到相应的WorkRequest
从而
//取消任务 方式1:
WorkManager.getInstance().cancelWorkById(workConstrains.id)
//取消任务 方式2: (这个方式我取消了但是不知道为什么任务都不会停止)
//这里传入的参数 true 如果线程执行, 任务应该被打断 false,允许在进行任务, 完成;
//WorkManager.getInstance().getWorkInfoById(workConstrains.id).cancel(true)
/** 通过在WorkRequestBuilder 设置 TAG 然后获取TAG进行取消任务 **/
OneTimeWorkRequest.Builder(BtnTwoWorker::class.java)
.addTag("TAG")
.setInputData(data)
.build()
//取消任务 方式2:
WorkManager.getInstance().getStatusesByTag("TAG")
//取消任务 具有特定标记的所有任务 **TAG可以相同**
WorkManager.getInstance().cancelAllWorkByTag("TAG")
多任务高级进阶使用(参考官网)
顺序执行
WorkRequest request1 = new OneTimeWorkRequest.Builder(FooWorker.class).build();
WorkRequest request2 = new OneTimeWorkRequest.Builder(BarWorker.class).build();
WorkRequest request3 = new OneTimeWorkRequest.Builder(BazWorker.class).build();
workManager.beginWith(request1, request2).then(request3).enqueue();
每次调用beginWith(OneTimeWorkRequest)
或beginWith(List)
返回一个WorkContinuation
,您可以在上调用 WorkContinuation.then(OneTimeWorkRequest)
或WorkContinuation.then(List)
链接进一步的工作。这允许创建复杂的工作链。例如,要创建这样的链:
WorkContinuation continuation = workManager.beginWith(A);
continuation.then(B).then(D, E).enqueue(); // A is implicitly enqueued here
continuation.then(C).enqueue();
Note: 完成所有先决条件后,工作才有资格执行。如果其任何先决条件失败或被取消,则该工作将永远不会进行。
- 任务唯一性
很多情况下,我们希望在任务队列里,同一个任务只存在一个,避免任务的重复执行,这时候可以用到beginUniqueWork
这个方法:
WorkManager.getInstance()
.beginUniqueWork("onlyOne", ExistingWorkPolicy.KEEP, work)
.enqueue()
三个参数 唯一名字、 冲突的处理方式、OneTimeWorkRequest的任务。
冲突的方式enum
类型:
REPLACE :如果有现有的等待(未完成)使用相同的惟一名称,取消和删除它。然后插入现有的任务。
KEEP:如果有现有的等待(未完成)使用相同的惟一名称,则什么也不做。 否则,插入现有的任务。
APPEND:如果有现有的等待(未完成)使用相同的惟一名称附加的, 新指定的工作作为一个孩子的所有的叶子工作序列。否则,插入现有的任务开始一个新的序列