前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记之Android架构组件-WorkManager

笔记之Android架构组件-WorkManager

作者头像
易帜
发布2022-02-09 16:10:55
8760
发布2022-02-09 16:10:55
举报
文章被收录于专栏:易帜的Android 学习之旅

service一直被用来做后台运行的操作,包括一些保活,上传数据之类的,这个后台运行的弊端很多,比如耗电,比如设计用户隐私之类的,谷歌对这些后台行为进行了一些处理,从Android Oreo(API 26) 开始,如果一个应用的目标版本为Android 8.0,当它在某些不被允许创建后台服务的场景下,调用了ServicestartService()方法,该方法会抛出IllegalStateException。并且出台了一些新政策:

1、2018年8月: 所有新开发应用的target API level必须是26(Android 8.0)甚至更高。 2、2018年11月: 所有已发布应用的target API level必须更新至26甚至更高。 3、2019年起: 在每一次发布新版本的Android系统之后,所有新开发以及待更新的应用都必须在一年内将target API level调整至对应的系统版本甚至更高。

如果想继续使用service,必须调用Context.startForegroundService(),在前台启动新服务,系统创建服务,应用有五秒的时间来调用该服务的 startForeground()方法以显示新服务的用户可见通知。 如果应用在此时间限制内未调用startForeground(),则系统将停止服务并声明此应用为 ANR。所以,在不久的将来,service的使用范围会越来越小,取而代之的,是谷歌推出的新的技术:WorkManagerWorkManager在工作的触发器 满足时, 运行可推迟的后台工作。WorkManager会根据设备API的情况,自动选用JobScheduler, 或是AlarmManager来实现后台任务,WorkManager里面的任务在应用退出之后还可以继续执行(注意:是程序退出,并非杀死进程还可以继续执行任务),这个技术适用于在应用退出之后任务还需要继续执行的需求,对于在应用退出的之后任务也需要终止的需求,可以选择ThreadPool、AsyncTask

首先添加依赖:

代码语言:javascript
复制
//如果创建后台任务请求,编译器抛出异常,则使用
android {
 kotlinOptions {
        jvmTarget = 1.8
    }
}

    // Kotlin + coroutines
    implementation "androidx.work:work-runtime-ktx:2.3.1"

WorkManager的简单使用

  1. 创建后台任务,并且实现具体的任务逻辑。
  2. 配置后台运行任务运行条件和约束条件,并且构建后台任务请求
  3. 将后天任务请求传入WorkManager.enqueue()中,系统会在合适的时间运行。

1. 创建后台任务

代码语言:javascript
复制
class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    //该方法不会运行在主线程,所以我们可以在此处进行耗时操作。
    override fun doWork(): Result {
        Log.e("TAG","start")
        Thread.sleep(2000)
        Log.e("TAG","end")
        return Result.success() //返回成功
//      return Result.failure() //返回失败
//      return Result.retry() //重试,其实也返回失败,只是与WorkRequest.Builder的setBackoffCriteria()结合后进行重新启动
    }

}

2. 构建后台任务请求

因为可配置的条件比较多,所以等一下讲解,现在进行基本用法。

代码语言:javascript
复制
  			// 对于一次性 WorkRequest,请使用 OneTimeWorkRequest,
  			//对于周期性工作,请使用 PeriodicWorkRequest
            // 构建一次性请求,下面是两种不同的创建创建方式
            val workRequest = OneTimeWorkRequestBuilder<MyWork>()
                .build()
          	
          	//1 小时进行一次周期性任务请求
            val periodicWorkRequestBuilder =
                PeriodicWorkRequestBuilder<MyWork>(1, TimeUnit.HOURS)
                    .build()

注意: 为了降低设备性能消耗,如果创建周期性的工作,那么其运行周期不能短与15分钟。

3.将后台任务请求,传递给WorkMnager的enqueue()

代码语言:javascript
复制
	//添加一次性请求任务
  WorkManager.getInstance(this)..enqueue(workRequest)
  	//添加周期性请求任务
  WorkManager.getInstance(this).enqueue(periodicWorkRequestBuilder)

4.取消和停止工作

代码语言:javascript
复制
   val workRequest = OneTimeWorkRequestBuilder<MyWork>()
                .addTag("workmanager")
                .build()
                
            WorkManager.getInstance(this).cancelAllWorkByTag("workmanager")
			WorkManager.getInstance().cancelWorkById(request.getId());
			//会返回 LiveData 和具有该标记的所有任务的状态列表
    		WorkManager.getInstance().getWorkInfosByTagLiveData(TAG); 

使用id只能取消单个后台任务请求,而使用标签的话,则可以将同一标签名的所有后台任务请求全部取消。

进阶使用

1.约束条件

Constraints.Builder的 API

代码语言:javascript
复制
        val uri = Uri.parse("xxxxx")
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED) //指定需要在有网的情况下
            .setRequiresBatteryNotLow(true)//指定电量在可接受范围内运行
            .setRequiresStorageNotLow(true)//指定在存储量在可接受范围内运行
            .addContentUriTrigger(uri, true)//当Uri发生变化的时候运行
            .setRequiresDeviceIdle(true)//当设备处于空闲状态时运行
            .setRequiresCharging(true)//当设备处于充电状态时运行
            .build()
        
        //创建请求
        val request = OneTimeWorkRequestBuilder<MyWork>()
            .setConstraints(constraints)//添加约束
            .build()
        //当满足约束条件后才会执行该任务
        WorkManager.getInstance(this).enqueue(request)

注意:如果指定了多个约束,你的任务将仅在满足所有约束时才会运行。

如果在任务运行期间某个约束不再得到满足,则 WorkManager 将停止工作器。当约束继续得到满足时,系统将重新尝试执行该任务。

2. 延时处理

代码语言:javascript
复制
  val workRequest = OneTimeWorkRequestBuilder<MyWork>()
                .setInitialDelay(1,TimeUnit.MINUTES) //延迟一分钟后执行
                .build()

3.设置回退/重试的策略,当doWork()返回Result.retry()时启用

代码语言:javascript
复制
  val workRequest = OneTimeWorkRequestBuilder<MyWork>()
                .setBackoffCriteria(BackoffPolicy.LINEAR,10,TimeUnit.MINUTES)
                .build()

该方法接受三个参数: 第一个参数:指定任务再次执行失败,下次重试的时间应该以什么样的形式延迟。这个很好理解,假如任务一直执行失败,不断地重新执行也没什么意义,只会徒增设备的性能消耗。而随着失败次数增多,下次重试的时间也应该进行适当的延迟。第一个参数可选值有两种,分别是LINEAREXPONENTIAL,前者表示下次重试时间以线性的方式延迟,后者代表下次重试时间为指数的方式延迟。 第二个参数:时间 第三个参数:时间类型 注意:最短时间不能少于10秒钟

4.传入参数/标记请求任务

代码语言:javascript
复制
   btn.setOnClickListener {
            //创建参数
            val imageData:Data=Data.Builder()
                .putString(DATA_KEY,"开始执行")
                .build()

            val request = OneTimeWorkRequestBuilder<MyWork>()
                .setInputData(imageData) //传入参数
                .build()

            mWorkManager.enqueue(request)
        }

class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {

    override fun doWork(): Result {
        val inputData = inputData
        var string = inputData.getString(DATA_KEY)
        Log.e("Tag",string)

        //创建输出结果
        val build = Data.Builder()
            .putString(DATA_KEY, "我接受到了参数了")
            .build();
        return Result.success(build)
    }

}

5.监听工作状态

代码语言:javascript
复制
  WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.getId())
                .observe(this, object : Observer<WorkInfo>{
                    override fun onChanged(@Nullable workInfo: WorkInfo?) {
                        if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
                            //获取成功返回的结果
                            Log.e("TAG",workInfo.outputData.getString(DATA_KEY))
                        }
                    }
                })

6. 指定并发或者按照顺序执行任务

代码语言:javascript
复制
        val imageData:Data=Data.Builder()
            .putString(DATA_KEY,"开始执行")
            .build()
        val imageData2:Data=Data.Builder()
            .putString(DATA_KEY,"开始执行1")
            .build()

        val request = OneTimeWorkRequestBuilder<MyWork>()
            .setInputData(imageData) //传入参数
            .build()

        val request1 =  OneTimeWorkRequestBuilder<MyWork>().
            setInputData(imageData2) //传入参数
            .build()
        val request2 = OneTimeWorkRequestBuilder<MyWork>().build()
        val request3 = OneTimeWorkRequestBuilder<MyWork>()
                .setInputMerger(OverwritingInputMerger::class.java)
                .build();
        val request4 =  OneTimeWorkRequestBuilder<MyWork>().build();
//        为了管理来自多个父级 OneTimeWorkRequest 的输入,WorkManager 使用 InputMerger。
//        WorkManager 提供两种不同类型的 InputMerger:
//        OverwritingInputMerger 会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。
//        ArrayCreatingInputMerger 会尝试合并输入,并在必要时创建数组。



        WorkManager.getInstance(this)
            //使用beginWith()可以并行执行request、request1、request2
            .beginWith(  mutableListOf<OneTimeWorkRequest>(request,request1,request2))
            //使用then()可以按顺序执行任务
            .then(request3)//在执行request3
            .then(request4)//在执行request4
            .enqueue();

这里我们需要注意一下如果我们使用这种链式调用的话,WorkManager还要求,必须在前一个后台任务运行完成之后,下一个后台任务才会运行。也就是说,如果某一个后台任务运行失败,或者取消,那么接下来的后台任务就都得不到运行了。

7.唯一工作序列

我们要想创建一个唯一的工作序列,只需调用beginUniqueWork()而不是beginWith().来开始序列。每个唯一的工作序列都有一个名字,WorkManager一次只允许一个工作序列使用该名称,当我们创建一个新的唯一工作序列时,如果已经有一个未完成的序列具有相同的名称,则指定WorkManager应执行的操作: 取消现有的序列并用新序列其替换 保持现有顺序并忽略新的请求 将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务 如果我们有一个不应该多次入队的任务,则唯一工作序列可能很有用。例如,如果我们的应用需要将其数据同步到网络,我们可能会排列一个名为“sync”的序列,并指定如果已经有一个具有该名称的序列,则应该忽略我们的新任务。如果我们需要逐步建立一个长期的任务链,那么唯一的工作序列也会很有用,例如,照片编辑应用可能会让用户撤消一长串的操作,每个撤销操作可能需要一段时间,但必须按正确的顺序执行,在这种情况下,应用程序可以创建一个“撤消”链并根据需要将每个撤销操作追加到链中。

如果进程被杀死,或者不满足约束条件时,那么WorkManager是不会运行的。当约束继续得到满足时,或者程序重新启动时,系统将重新尝试执行该任务。 例子:

代码语言:javascript
复制
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {

        textViewA.text=sharedPreferences?.getInt("A", 0).toString()
        textViewB.text=sharedPreferences?.getInt("B", 0).toString()
    }

    private val mWorkManager by lazy {
        WorkManager.getInstance(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val sharedPreferences = getSharedPreferences("WORK", 0)
        sharedPreferences.registerOnSharedPreferenceChangeListener(this)
        btn.setOnClickListener {
            val build = OneTimeWorkRequestBuilder<MyWork>()
                .setInputData(workDataOf("A" to "A"))
                .build()
            val build2 = OneTimeWorkRequestBuilder<MyWork>()
                .setInputData(workDataOf("A" to "B"))
                .build()

            mWorkManager.beginWith(build)
                .then(build2)
                .enqueue()
        }


    }
}

open class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {

    override fun doWork(): Result {
        Thread.sleep(6000)
        val sp =applicationContext.getSharedPreferences("WORK", 0)
        val key = inputData.getString("A")

        var int = sp.getInt(key, 0)
        ++int;

        sp.edit().putInt(key,int).apply()
        return Result.success()
    }

}

如果有什么地方有问题,也欢迎大家提出。

参考博客:

Android Jetpack架构组件之WorkManager入门 WorkManager 入门指南 WorkManger

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 创建后台任务
  • 2. 构建后台任务请求
  • 3.将后台任务请求,传递给WorkMnager的enqueue()
  • 4.取消和停止工作
  • 进阶使用
    • 1.约束条件
      • 2. 延时处理
        • 3.设置回退/重试的策略,当doWork()返回Result.retry()时启用
          • 4.传入参数/标记请求任务
            • 5.监听工作状态
              • 6. 指定并发或者按照顺序执行任务
                • 7.唯一工作序列
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档