Android 现在还能执行后台任务吗?试试 WorkManager 吧

640?wx_fmt=jpeg


/   今日科技快讯   /


近日有消息称特斯拉获得中国有关部门批准,将在上海工厂开始生产Model 3。对特斯拉而言,这是一个非常重要的里程碑,因为它将是第一家在中国由外国汽车制造商全资拥有的纯电动汽车工厂。


/   作者简介   /


本篇文章来自新司机 未扬帆的小船 的投稿,分享了他对Jetpack框架中WorkManager相关内容的整理,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。


未扬帆的小船的博客地址:
https://www.jianshu.com/u/3994846b6c55


/   前言   /


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"


简单使用方式


实现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.
    }
}

调用

由OneTimeWorkRequest.Builder().build()创建对象WorkRequest,然后将任务放入的WorkManager管理的WorkRequest队列(本次文章中都以OneTimeWorkRequest来写例子 )

 
  
val baseWork = OneTimeWorkRequest.Builder(MainWorker::class.java).build()
WorkManager.getInstance().enqueue(baseWork)

这样子简单的使用方式就可以了~~~~

但是如果你要监听数据信息然后获取数据进行逻辑操作呢(数据交互)?

可以看一下下面的使用。

使用方式2 : 监听数据方式

实现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)
    }
}

使用

代码中的第一步构建的就是 步骤一中的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);
}

当任务显示成功/失败/取消,都是结束~ 

增加任务约束的使用

使用Constraints.Builder()创建并配置Constraints对象,里面的设置信息在API中皆有体现。如下图:

640?wx_fmt=other

  • 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)链接进一步的工作。这允许创建复杂的工作链。例如,要创建这样的链:


640?wx_fmt=other


 
  
WorkContinuation continuation = workManager.beginWith(A);
continuation.then(B).then(D, E).enqueue();  // A is implicitly enqueued here
continuation.then(C).enqueue();

完成所有先决条件后,工作才有资格执行。如果其任何先决条件失败或被取消,则该工作将永远不会进行。

任务唯一性

很多情况下,我们希望在任务队列里,同一个任务只存在一个,避免任务的重复执行,这时候可以用到 beginUniqueWork 这个方法:

 
  
WorkManager.getInstance()
        .beginUniqueWork("onlyOne", ExistingWorkPolicy.KEEPwork)
        .enqueue()

三个参数分别指唯一名字、 冲突的处理方式、OneTimeWorkRequest的任务。冲突的方式enum类型:

  • REPLACE:如果有现有的等待(未完成)使用相同的惟一名称,取消和删除它。然后插入现有的任务。
  • KEEP:如果有现有的等待(未完成)使用相同的惟一名称,则什么也不做。否则,插入现有的任务。
  • APPEND:如果有现有的等待(未完成)使用相同的惟一名称附加的, 新指定的工作作为一个孩子的所有的叶子工作序列。否则,插入现有的任务开始一个新的序列。


推荐阅读:




欢迎关注我的公众号
学习技术或投稿


640.png?


640?wx_fmt=jpeg
长按上图,识别图中二维码即可关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值