jetpack-workmanager 使用及源码分析

2022-06-13  本文已影响0人  付小影子

使用案例

使用分为三步骤
1.创建自定义worker,处理后台任务
2.利用建造者模式 构建WorkRequest(抽象类) 请求,有两个实现类
OneTimeWorkRequest单次执行任务,执行一次就结束了
PeriodicWorkRequest 多次循环执行任务
构建请求的时候,还可以添加约束条件setConstraints(@NonNull Constraints constraints)
3.WorkManager 把请求加入队列

自定义worker--简单案例

class MainWork1(val context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    //后台任务,并且是 异步放入,线程池执行runnable
    override fun doWork(): Result {
        Log.d("hh", "mainWork1 doWork:run starting...")
        //Toast.makeText(context,"start",Toast.LENGTH_SHORT).show()
        try {
            Thread.sleep(5000)//睡眠
        } catch (e: Exception) {
            e.printStackTrace()
            return Result.failure()

        } finally {
            Log.d("hh", "mainWork1 doWork:run ending...")
        }

        //failure retry 三种结果
        return Result.success();

    }
}

自定义worker--回传数据

class MainWork2(val context: Context, val workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    //后台任务,并且是 异步放入,线程池执行runnable
    override fun doWork(): Result {
        Log.d("hh", "mainWork1 doWork:run starting...")
        //Toast.makeText(context,"start",Toast.LENGTH_SHORT).show()
        //获取传递的数据
        val dataString = workerParams.inputData.getString("params")

        //回传数据
        val outputData = Data.Builder().putString("name", "付小影子").build()

        //Result success(@NonNull Data outputData)
        return Result.success(outputData);

    }
}

调用请求

class WorkManagerActivity : AppCompatActivity(),
    SharedPreferences.OnSharedPreferenceChangeListener {
    lateinit var sp: SharedPreferences

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_work_manager)
        //获取sp,并且设置监听
        sp = getSharedPreferences(MainWork6.SP_NAME, MODE_PRIVATE);
        sp.registerOnSharedPreferenceChangeListener(this)


        /**
         * 简单执行任务
         */
        btn1.setOnClickListener {
            Toast.makeText(this@WorkManagerActivity, "click", Toast.LENGTH_SHORT).show()
            //创建请求 Class<? extends ListenableWorker> workerClass
            val request = OneTimeWorkRequest.Builder(MainWork1::class.java).build()
            //把请求加入队列
            WorkManager.getInstance(this).enqueue(request)

        }

        /**
         * 回传数据
         */
        btn2.setOnClickListener {
            //构建传递的数据
            val sendData = Data.Builder().putString("params", "123456").build()
            val request =
                OneTimeWorkRequest.Builder(MainWork2::class.java).setInputData(sendData).build()

            //利用状态机(LiveData 只有页面处于活动状态 才会接受数据) 才能接受workManager回馈的数据
            WorkManager.getInstance(this@WorkManagerActivity).getWorkInfoByIdLiveData(request.id)
                .observe(this@WorkManagerActivity, {

                    //enum State ENQUEUED  RUNNING SUCCEEDED FAILED BLOCKED CANCELLED 六种状态
                    Log.d("hh", it.state.name)

                    //根据回传的状态,确定数据 Result.success(outputData);
                    // Result failure(@NonNull Data outputData)
                    // (this == SUCCEEDED || this == FAILED || this == CANCELLED)
                    if (it.state.isFinished) {
                        it.outputData.getString("name")?.let { it1 -> Log.d("hh", it1) }
                    }
                })
            WorkManager.getInstance(this@WorkManagerActivity).enqueue(request)

        }

        /**
         * 多个任务顺序执行
         */
        btn3.setOnClickListener {
            //顺序执行任务 work3  work4  work5
            val request1 = OneTimeWorkRequest.Builder(MainWork3::class.java).build()
            val request2 = OneTimeWorkRequest.Builder(MainWork4::class.java).build()
            val request3 = OneTimeWorkRequest.Builder(MainWork5::class.java).build()
            //beginWith(@NonNull OneTimeWorkRequest work)
            WorkManager.getInstance(this).beginWith(request1)
                .then(request2)
                .then(request3)
                .enqueue()

            //先执行work3,5  最后执行work4
            //beginWith(@NonNull List<OneTimeWorkRequest> work)
            val requestList = listOf<OneTimeWorkRequest>(request1, request3)
            WorkManager.getInstance(this).beginWith(requestList)
                .then(request2)
                .enqueue()

        }

        //重复任务 多次 循环执行,轮循
        btn4.setOnClickListener {
            //OneTimeWorkRequest 是单次执行任务,不会轮循,都继承WorkRequest,抽象类

            //重复的,多次的,循环的任务
            //为了省电优化,设置的默认时间是15分钟。所以即使我们配置为10s 执行一次后台任务,也会是15分钟执行
            val request =
                PeriodicWorkRequest.Builder(MainWork3::class.java, 10, TimeUnit.SECONDS).build()

            //因为是多次轮循,所以一直都拿不到success结果,不出错不取消的情况下,一直是 ENQUEUED  RUNNING来回切换
            WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id)
                .observe(this, {
                    Log.d("hh", it.state.name) //不出错不取消的情况下,一直是 ENQUEUED  RUNNING来回切换
                    if (it.state.isFinished) {
                        Log.d("hh", "任务执行结束....")
                    }
                })
            //加入队列
            WorkManager.getInstance(this).enqueue(request)
            //取消任务,只是表面的取消,任务还是得执行完成 cancelWorkById(request.id) cancelAllWork() cancelAllWorkByTag()
            //WorkManager.getInstance(this).cancelWorkById(request.id)
        }

        /**
         * 添加约束条件,执行后台任务
         */
        btn5.setOnClickListener {
            //定义约束条件
            val constrains = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED) //必须是联网状态
                .setRequiresCharging(true)//添加 充电中状态
                .setRequiresDeviceIdle(true)//添加闲时状态
                .setRequiresBatteryNotLow(true)//不是低电量状态
                .build()

            //创建请求,绑定约束条件
            val request =
                OneTimeWorkRequest.Builder(MainWork3::class.java).setConstraints(constrains).build()

            //加入队列
            WorkManager.getInstance(this).enqueue(request)
        }

        /**
         * 测试 任务一直在后台运行。。利用sp写入数据,刷新到页面上
         */
        btn6.setOnClickListener {
            //定义约束条件
            val constrains = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED) //必须是联网状态
                .build()

            //创建请求,绑定约束条件
            val request =
                OneTimeWorkRequest.Builder(MainWork6::class.java).setConstraints(constrains).build()

            //加入队列
            WorkManager.getInstance(this).enqueue(request)

        }
    }

    //当sp 文件内容被改变时候,会回调此函数
    override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) {
        tvValue.text = "后台任务数据--${sp.getInt(MainWork6.SP_KEY, 0)}"
    }
}

常见面试提问

workmanager 是干嘛用的?

处理非及时任务的,比如每天同步一次数据到服务器,这种类似的需求,不是及时执行,但是又保证一定会被执行的非及时任务。

workmanager 是怎么保证,把APP退出后,还能正常运行?

记录用户的所有任务信息,并且保存的数据库中,没有保存在内存中,就是为了持久性保存记录。所有APP被杀掉后,依然可以获取所以的任务信息。然后通过广播和系统级别的服务(SystemAlertService),进行任务执行。

workmanager 任务是怎么保证一定会执行的?

Android 操作系统会在系统级别的服务中,来判断用户的约束条件,当约束条件满足时,就会执行任务,但是触发检测是采用广播的形式处理的,比如网络连接成功就触发等。

源码解析

workmanager注入说明

打包apk的时候,Android 系统会在配置清单文件中生成ContentProver作为workmanager的入口配置,执行第一次初始化。利用系统级别service,即使APP退出后台,仍旧能保证任务的执行。


111.png

源代码代码执行流程

222.png 333.png 444.png

广播执行的流程

添加的约束条件,会生成很多广播,这些广播只是个空壳子,并没有实现操作。创建了个总广播,来处理各种广播receive。


555.png
上一篇下一篇

猜你喜欢

热点阅读