WorkManager使用小技巧

6 篇文章 0 订阅
3 篇文章 0 订阅

android jetpack 提供的包中提供的新工具:WorkManager

//workManager引入
implementation 'androidx.work:work-runtime:2.5.0' //java

implementation 'androidx.work:work-runtime-ktx:2.5.0' //kotlin

除了官方说的持久性工作用途

 我发现使用WorkManager + Retrofit可以帮助我们优雅的完成网络请求事件

        例如

        我们有一个登录的请求操作,需要将账号+密码上传给服务器进行验证,并将结果反馈到主线程。首先在Android环境下,请求网络这种事情是不能在主线程的,所以一般的做法是单独为其开一个子线程来做请求操作,待请求结果完成,再返回给主线程。这中间所有关于线程切换的操作,都需要我们手动完成,而且中途如果子线程抛出异常,我们是很难处理的,而且还会造成大量的代码冗余。

        但是结合WorkManager + Retrofit之后情况就完全不同啦,不废话,上代码:

/**
 * 登录任务
 */
class LoginWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {

    companion object {
        //两个Key值
        const val LOGIN_NAME = "LOGIN_NAME"
        const val LOGIN_PWD = "LOGIN_PWD"

        /**
         * 在伴生对象中(相当于java的静态方法)用方法创建一个WorkRequest
         * 这个request是一个一次性的Work任务
         */
        fun start(name: String, pwd: String): WorkRequest {
            val workRequest = OneTimeWorkRequestBuilder<LoginWorker>().apply {
                setInputData(Data.Builder().let {
                    //通过setInputData往内部填入账号+密码
                    it.putString(LOGIN_NAME, name)
                    it.putString(LOGIN_PWD, pwd)
                    it.build()
                })
            }.build()
            return workRequest
        }
    }

    /**
     * Worker对象实际执行任务的地方
     */
    override fun doWork(): Result {

        //首先从inputData中取出账号+密码
        val name: String? = inputData.keyValueMap[LOGIN_NAME] as String?
        val pwd: String? = inputData.keyValueMap[LOGIN_PWD] as String?

        //如果为空,则直接返回任务失败
        if (name == null || pwd == null)
            return Result.failure(
                Data.Builder().apply {
                    //任务失败也是可以设置数据的,存入失败原因或其它任何你想要配置的数据
                    putInt(ConstantUtil.WORKER_RESULT_CODE_KEY, -1)
                    putString(
                        ConstantUtil.WORKER_RESULT_KEY,
                        "name == null || pwd == null"
                    )
                }.build()
            )

        //这里因为是从DataStore中取数据,所以用了协程
        //不了解协程的小伙伴可以忽略,不影响
        val netId = runBlocking { DataStoreUtil.get<Int>(ConstantUtil.LOGIN_NET_ID_KEY) }
        
        LOG.info("Login Worker netId = $netId")

        //这里通过Retrofit来实现网络请求
        val call =
            RetrofitUtil.retrofit.create(LoginService::class.java).login(name = name, pwd = pwd, netId = netId)
        call?.execute()?.let {
            //执行完成后,根据结果来判断是非登录成功
            if (it.body()!!.code != 0) {
                LOG.error("LoginWorker -> result code = ${it.body()!!.code}")
                LOG.error("LoginWorker -> result msg = ${it.body()!!.msg}")
                //登录失败
                return Result.failure(
                    Data.Builder()
                        .apply {
                            putInt(ConstantUtil.WORKER_RESULT_CODE_KEY, -2)
                            putString(ConstantUtil.WORKER_RESULT_KEY, "result code != 0")
                        }
                        .build()
                )
            }

            //登录成功后的token保存至DataStore
            runBlocking { DataStoreUtil.put(ConstantUtil.LOGIN_TOKEN_KEY, it.body()!!.data) }

            //登录成功的结果反馈
            return Result.success(Data.Builder().apply {
                putInt(ConstantUtil.WORKER_RESULT_CODE_KEY, it.body()!!.code)
                putString(ConstantUtil.WORKER_RESULT_KEY, it.body()!!.data)
            }.build())
        }

        return Result.failure(
            Data.Builder()
                .apply {
                    putInt(ConstantUtil.WORKER_RESULT_CODE_KEY, -3)
                    putString(ConstantUtil.WORKER_RESULT_KEY, "cannot get response")
                }.build()
        )
    }

}

retrofit本身就不讲了,网上很多使用教程,我这边贴一下我定义的接口就行了

interface LoginService {

    @POST(RetrofitUtil.headUrl)
    @FormUrlEncoded
    fun login(
        @Field("m") m: String = "LOGIN",
        @Field("name") name: String,
        @Field("pwd") pwd: String,
        @Field("netId") netId: Int
    ): Call<LoginResultBean?>?

    @POST(RetrofitUtil.headUrl)
    @FormUrlEncoded
    fun checkLogin(
        @Field("m") m: String = "check_token",
        @Field("token") token: String
    ): Call<LoginResultBean?>?

    @GET(RetrofitUtil.headUrl)
    fun getUserInfo(
        @Query("m") m: String = "USER_INFO",
        @Query("token") token: String
    ): Call<UserInfoBean?>?

}

上面的代码是对于登录这个行为的定义,或者说预设,接下来讲讲使用

        //通过伴生方法,直接创建一个workRequest
        val workRequest = LoginWorker.start(name, pwd)

        //WorkManager是支持lifeCycle的
        //所以通过workRequest的id,对任务的执行做观察方法
        WorkManager.getInstance(requireContext())
            .getWorkInfoByIdLiveData(workRequest.id)
            .observe(requireActivity()) {

                LOG.e("当前work的执行状态->${it.state}")
                if (it.state != WorkInfo.State.SUCCEEDED && it.state != WorkInfo.State.FAILED) {
                    LOG.e("不关心其它状态")
                    return@observe
                }
                
                //这里就是对结果数据进行解析啦
                val data = it.outputData.getString(ConstantUtil.WORKER_RESULT_KEY)
                val code = it.outputData.getInt(ConstantUtil.WORKER_RESULT_CODE_KEY, -11)
                LOG.info("LoginFragment login Result code->$code")
                LOG.info("LoginFragment login Result data->$data")
                if (code == 0) {
                    //登录成功
                    loginSucceed()
                    sweetDialogDismiss()
                    Toast.makeText(requireContext(), "登录成功", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(requireContext(), data, Toast.LENGTH_SHORT).show()
                    sweetDialog().apply {
                        titleText = "登录失败"
                        contentText = data
                        changeAlertType(SweetAlertDialog.ERROR_TYPE)
                    }.show()
                }
            }
        //这里表示将workRequest这个任务正式加入WorkManager的队列中,由其决定怎么执行
        WorkManager.getInstance(requireContext()).enqueue(workRequest)

总结:

        通过WorkManager我们可以非常轻松的对日常开发的任务进行预设置,也就通过继承Worker来对某一行为的预置,然后通过伴生方法或静态方法实现快速构建,这样代码逻辑就变得极其清晰了。特别是针对网络请求这类行为,WorkManager自带的线程管理机制要比我们自己重新写一套线程切换机制简单的多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值