Kotlin之协程——在高阶函数中使用协程&将协程与WorkManager结合使用

1、在高阶函数中使用协程

您将重构MainViewModel中的refreshTitle,以使用常规的数据加载函数。

refreshTitle的当前实现运行正常,但我们可以创建一个始终显示旋转图标的常规数据加载协程。对于加载数据以影响多个事件且希望确保加载旋转图标始终显示的代码库,这可能非常有用。

查看当前实现,可以发现,除reppository.refreshTitle()之外的每行代码都是显示旋转图标和错误的样板代码。

// MainViewModel.kt
fun refreshTitle() {
    viewModelScope.launch {
        try {
            _spinner.value = true
            // this is the only part that changes between sources
            repository.refreshTitle()
        } catchh (error: TitleRefreshError) {
            _snackBar.value = error.message
        } finally {
            _spinner.value = false
        }
    }
}

重要提示:虽然我们在此代码中仅适用viewModelScope,但通常可以在任何意义的位置添加作用于。如果不再需要它,记得将其取消。

例如,您可以在RecyclerView Adapter中声明一个作用域,以执行DiffUtil操作。

1.1、在高阶函数中使用协程

将以下代码添加到MainViewModel.kt中 MainViewModel.kt

private fun launchDataLoad(block: suspend () -> Unit): Job {
    return viewModelScope.launch {
        try {
            _spinner.value = true
            block()
        } catch (error: TitleRefreshError) {
            _snackBar.value = error.message
        } finally {
            _spinner.value = false
        }
    }
}

现在重构refreshTitle()以使用此高阶函数。 MainViewModel.kt

fun refreshTitle() {
    launchDataLoad {
        repository.refreshTitle()
    }
}

通过抽象化用于显示加载旋转图标和显示错误的逻辑,我们简化了加载数据所需的实际代码。显示旋转图标或显示错误是易于泛化到任何数据加载的内容,而实际数据源和目标则需要每次都指定。

为了构建此抽象,launchDataLoad接受一个属于挂起lambda的参数block。挂起lambda可让您调用挂起函数。Kotlin就是通过这种方式实现我们在此代码中 使用的launchrunBlocking协程构建器。

// suspend lambda

block: suspend () -> Unit

要创建挂起lambda,应从suspend关键字着手。函数的箭头和返回值类型Unit用于完成声明。

您通常不必声明自己的挂起lambda,但它们可能有助于创建这类封装重复逻辑的抽象!

2、将协程与WorkManager结合使用

本部分,您将学习如何从WorkManager使用基于协程的代码。

2.1、什么是WorkManager

Android有多个选项用于处理可延迟的后台工作。本练习介绍如何将WorkManager与协程集成。WorkManager是一个兼容、灵活且简单的库,用于处理可延迟的后台工作。WorkManager是Android中这些用例的推荐解决方案。

WorkManager属于Android Jetpack的一部分,是一种架构组件,用于处理既需要机会性执行,又需要有保证的执行的后台工作。机会性执行意味着WorkManager会尽快执行您的后台工作。有保证的执行意味着WorkManager会负责通过逻辑保障在各种情况下启动您的工作,即使用户离开您的应用也无妨。

因此,WorkManager适合最终必须完成的任务。

以下是一些适合使用WorkManager的任务的典型示例:

  • 上传日志
  • 对图片应用滤镜并保存图片
  • 定期将本地数据与网络同步

2.2、将协程与WorkManager一起使用

WorkManager为不同用例提供其基本ListenableWorker类的不同实现。

最简单的Worker类可让我们通过WorkManager执行一些同步操作。不过,根据目前为止我们将代码库转换为使用协程和挂起函数的经验,我们发现使用WorkManager的最好方法是通过CoroutineWorker类,此类支持将doWork()函数定义为挂起函数。

首先,请打开refreshMainDataWork。它已扩展了CoroutineWorker,您需要实现doWork

suspend doWork函数中,从代码库中调用refreshTitle()并返回响应的结果。

完成TODO后,代码将如下所示。

override suspend fun doWork(): Result {
    val database = getDatabase(applicationContext)
    val repository = TitleRepository(network, database.titleDao)
    
    return try {
        repository.refreshTitle()
        Result.success()
    } catch (error: TitleRefreshError) {
        Result.failure()
    }
}

请注意,CoroutineWorker.doWork()是一个挂起函数。与更简单的Worker类不同,此代码不会在您的WorkManager配置所指定的执行器上运行,而是使用coroutineContext成员的调度程序(默认为Dispatchers.Default

2.3、测试我们的CoroutineWorker

WorkManager提供了几种不同的Worker类测试方法。

WorkManager v2.1引入了一组新的API来支持更简单的ListenableWorker类测试方法,并最终推出了CoroutineWorker。我们将在代码中使用一个名为TestListenableWorkerBuilder的新API。

为了添加我们的新测试,更新androidTest文件夹下的RefreshMainWorkTest文件。

此文件的内容为:

package com.example.android.kotlincoroutines.main

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker.Result
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker.Result
import androidx.work.testing.TestLiestenableWorkerBuilder
import com.example.android.kotlincoroutines.fakes.MainNetworkFake
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Junit4

@RunWith(JUnit4::class)
class RefreshMainDataWorkTest {

@Test
fun testRefreshMainDataWork() {
    val fakeNetwork = MainNetworkFake("OK")
    
    val context = ApplicationProvider.getApplicationContext<Context>()
    val worker = TestListenableWorkerBuilder<RefreshMainDataWork>(context)
        .setWorkerFactory(RefreshMainDataWork.Factory(fakeNetwork))
        .build()
        
    // Start the work synchronously
    val result = worker.startWork().get()
    
    assertThat(result).isEqualTo(Result.success)
}
}

在执行测试之前,我们会先告知WorkManager关于工厂的信息,以便我们注入虚构网络。

测试本身会使用TestListenableWorkerBuilder创建我们的工作器,然后我们可以运行该工作器来调用startWork()方法。

WorkManger只是用来说明如何使用协程简化API设计的一个示例。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值