zoukankan      html  css  js  c++  java
  • Android Architecture Components 系列(七)WorkManager

    近期Google的Architecture Component 库 又迎来了两个新的组件:Navigation 、WorkManager 
     
    WorkManager定义
        管理一些要在后台工作的任务 ,在没有启动app的情况下保证任务能被执行。
    ps:这里有没有想起一些其他概念!!比如 守护进程!!互相调用进程!!!虚拟进程!!!
     
    问题一:有些人提出为什么不用JobScheduler 和AlarmManager?
        JobScheduler 是Android 5.x +才有的新方法,而AlarmManager是早就存在的方法。所以WorkManager会根据设备情况来选用JobScheduler 还是Firebase的JobDispatcher(后续学习一些)或是AlarmManager。
     
     
    问题二: 为什么不用AsyncTask,ThreadPool,RxJava?
        这三个线程管理工具和WorkManager 并不是提供关系或是说功能并不一样。前三者可以帮你在应用中开后台线程干活,但是app一旦被kill掉或是shutdown,这三个线程管理工具也就跟着stop工作了。而WorkManager就是为了弥补这方面的缺陷,在app被kill掉后甚至设备重启后仍能保证继续进行需要的任务。
     
    ps:其实Google自己也说了:"WorkManager并不是为了那种在应用内的后台线程而设计出来的. 这种需求你应该使用ThreadPool"
     
    WorkManager构建
     
        app/build.gradle 
     
        kotlin:
    implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha01"
     
        Android Java
    implementation "android.arch.work:work-runtime:1.0.0-alpha01"
     
     
    WorkManager 实例
        讲述一个小的定时推送的小案例
            工作步骤
    •     后台pull数据到前台 (确定要干什么活)
    •     将前台的数据加入活动队列已显示 (让这个活入队列)
     
    (1) Worker是干活的主体方法,它只管轮到了它时要做的工作,不考虑其他问题
    构建Worker 子类 并重写doWork()方法
      class PullWorker : Worker() { 
        
            override fun doWork(): WorkerResult { 
            // 模拟设置页面中的"是否接受推送"是否被勾选
             val isOkay = this.inputData.getBoolean("key_accept_bg_work", false) 
                 if(isOkay) { 
                    Thread.sleep(5000) //模拟长时间工作 
                    val pulledResult = startPull() 
                    val output = Data.Builder().putString("key_pulled_result", pulledResult).build()
                     outputData = output 
                    return WorkerResult.SUCCESS
                 } else { 
                    return WorkerResult.FAILURE 
                 } 
            } 
            fun startPull() : String{ 
                return "szw [worker] pull messages from backend”
             } 
      }
     
    (2)将Worker包装成一个WorkRequest ,并加入队列
        WorkRequest 一些新属性:
    • ID(一般是一个UUID, 以保证唯一性),
    • 何时执行,
    • 有没有限制(如只有在充电并连网时才执行此任务),
    • 执行链 (当某任务执行完了, 才能轮到我执行)
    WorkManager就负责把WorkRequest入列
     class PullEngine {
        fun schedulePull(){
            //java就请用PeriodicWorkRequest.Builder类
            val pullRequest = PeriodicWorkRequestBuilder<PullWorker>(24, TimeUnit.HOURS)
                    .setInputData(
                        Data.Builder()
                            .putBoolean("key_accept_bg_work", true)
                            .build()
                    )
                    .build()
            
            WorkManager.getInstance().enqueue(pullRequest)
        }
    }
     

    讲解

    1.干活的是Worker类. 我们一般是新建个Worker的子类, 并重写doWork()方法. 但是, doWork()方法是没有参数的. 我们有时有参数的需求,怎么办? 这时就要用上Worker.getInputData()方法了.
    2.同理, doWork()方法是返回void的. 你要是有结果想传出去, 就可以用Worker.setOutputData()
    3.上面的两个方法所得到/设置的数据类型都是Data. 这个Data很类似我们Android中的Bundle, 也有putInt(key, value), getString(key, defaultValue)这样的方法.
    一般Data的生成, 是用Data.Builder类. 如:
    val output = Data.Builder().putInt(key, 23).build()
    4.上面讲了WorkRequest其实就是入列的一个实体, 它包装了Worker在内. 但我们一般不直接使用WorkReqeust类, 多是用它的子类: OneTimeWorkRequest, 或是PeriodWorkReqeust.
    因为我们的pull需求是每天都要去拉一次, 所以这里我们没有用OneTimeWorkRequest, 而是构建了一个24小时就重复干活的PeriodicWorkReqeust.

    进阶

    1. 想拿到结果

    WorkManager提供了一个接口让我们拿到结果, 这个东东就是WorkStatus. 你可以由id得到你想要的那个任务的WorkStatus. 这个WorkStatus其实就是知道这任务没有完成, 有什么返回值.
    因为前后台要解耦合的原因, 所以这个工作其实是由LiveData来完成的. 既然有LiveData, 那我们肯定要有一个LifecycleOwner了(一般是我们的AppcompatActivity).
    来看个例子. 以上面的pull例子为例, 若我们拉到了结果, 就显示一个notification (这里为简便, 是收到结果后就打印一下日志).
    [PullEngine.kt]
    class PullEngine {
        fun schedulePull(){
            val pullRequest = PeriodicWorkRequestBuilder<PullWorker>(24, TimeUnit.HOURS).build()
            WorkManager.getInstance().enqueue(pullRequest)
     
            // 下面两行是新加的, 用来存任务的ID
            val pullRequestID = pullRequest.id
            MockedSp.pullId = pullRequestID.toString() // 模拟存在SharedPreference中
        }
    }
    [PullActivity.kt]
    class PullActivity : AppCompatActivity() {
     
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
     
            // UUID实现了Serializable接口. 也能由toString(), fromString()与String互转
            val uuid = UUID.fromString(MockedSp.pullId)
            WorkManager.getInstance().getStatusById(uuid)
                    .observe(this, Observer<WorkStatus> { status ->
                        if (status != null){
                            val pulledResult = status.outputData.getString("key_pulled_result", "")
                            println("szw Activity getResultFromBackend : $pulledResult")
                        }
                    })
        }
    }
    注意, observe()方法是用来监听嘛. 它的参数分别是: observer(LifecycleOwner, Observer<WorkStatus>)

    2. 总结入参/返回值

    入参: WorkRequest.Builder.setInputData()
    Worker类: 可以getIntpuData(), 以及setOutputData()
    返回值: 由LiveData监听, 可以得到WorkStatus. 而WorkStatus就有getOutputDat()方法
    只是注意,这里说的inputData, outputDat, 都不是普通的int, string. 而是Data类.

    3. 如果任务执行完了, 应用却没被启动怎么办? 会强行启动应用来显示UI变化吗?

    : 好问题. 但严格来说, 这个其实不是WorkManager的问题, 而是LiveData的问题. LiveData自己本身就是和Activity的生命周期绑定的. 你不用说应用被杀了, 就是你退出了这个注册的Activity, 你都收不到LiveData的通知. 所以说你的应用被杀, 任务又执行完了时, 是没有UI通知的, 更不会强行启动你的启动. (这有点流氓~)

    4. 任务链

    WorkManager.getInstance()
        .beginWith(workA)
        .then(workB)
        .then(workC)
        .enqueue()
    这样就会按workA, workB, workC的顺序来执行. workA执行完了, 才会接着执行workB.
    WorkManager甚至还能执行:
    A --> B
            --> E
    C --> D
    这样的形式, 即A执行完了才执行了B, C执行完才执行D. B,D都执行完了才执行E.

    5. 插入任务时, 已经有相同的任务时, 怎么办?

    WorkManager可以用beginUniqueWork()来执行唯一工作队列("unique work sequence"). 若有任务有重复时, 怎么办?
    这个主要是一个ExistingWorkPolicy类. 这个类也是WorkManager包中的类. 它其实是一个Enum. 其值有:
    • REPLACE: 用新任务来取代已经存在的任务
    • KEEP: 保留已经存在的任务. 忽视新任务
    • APPEND: 新任务入列. 新旧任务都存在于队列中.

     总结

    总体来说, WorkManager并不是要取代线程池/AsyncTask/RxJava. 反而是有点AlarmManager来做定时任务的意思. 即保证你给它的任务能完成, 即使你的应用都没有被打开, 或是设备重启后也能让你的任务被执行.
    WorkManager在设计上设计得比较好. 没有把worker, 任务混为一谈, 而是把它们解耦成Worker, WorkRequest. 这样分层就清晰多了, 也好扩展. (如以后再有一个什么WorkRequest的子类出来)
    最后, WorkManager的入参出参设计得不错. WorkReqeust负责放入参数, Worker处理并放置返回值, 最后WorkStaus中取出返回值, 并由LiveData来通知监听者.
    至于链式执行, 唯一工作队列这些特性在你有类似的需求时, 也能帮助到你.
     
     
    转自:
  • 相关阅读:
    node md5
    Iptables入门教程
    centos 6.5 yum安装lnmp
    SSH日志位置
    【收藏】实战Nginx与PHP(FastCGI)的安装、配置与优化
    读《微博春晚背后的技术故事》笔记
    下载整个网页的方法,包括样式、图片、和js
    【收藏】下载Chrome商店插件的方法,万恶的gwd
    nginx变量
    【收藏】SSH原理与运用
  • 原文地址:https://www.cnblogs.com/cold-ice/p/9115864.html
Copyright © 2011-2022 走看看