前文参考:
在 Android 应用中使用 Kotlin 协程 - 官方示例详解
在 Android 应用中使用 Kotlin 协程 - 官方示例详解(2)
1. Room 中的协程
MainDatabase.kt 中将 insertTitle 设置为挂起函数
// add the suspend modifier to the existing insertTitle
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTitle(title: Title)
分析:
执行此操作后,Room 会让查询具有主线程安全性,并自动在后台线程上执行此查询。
不过,这也意味着您只能从协程内调用此查询。
2. Retrofit 中的协程
MainNetwork.kt 将 fetchNextTitle 设置为 挂起函数.
将返回值类型从 Call<String> 更改为 String。
挂起函数支持需要 Retrofit 2.6.0 或更高版本。
// add suspend modifier to the existing fetchNextTitle
// change return type from Call<String> to String
interface MainNetwork {
@GET("next_title.json")
suspend fun fetchNextTitle(): String
}
分析:
(1) 为函数添加挂起修饰符
(2) 返回值类型中移除 Call 封装容器
Retrofit 将 自动使挂起函数具有 主线程安全性,
以便直接从 Dispatchers.Main 调用它们
Room 和 Retrofit 均会让挂起函数具有主线程安全性。
尽管这些挂起函数从网络中提取数据并将数据写入数据库,
您可以安全地从 Dispatchers.Main 调用这些函数。
Room 和 Retrofit 都使用自定义调度程序,而不使用 Dispatchers.IO。
Room 会使用已配置的默认查询和事务 Executor 运行协程。
Retrofit 将在后台创建新的 Call 对象,并对其调用队列以异步发送请求。
3. 使用 Room 和 Retrofit
TitleRepository.kt
原始:
// interact with *blocking* network and IO calls from a coroutine
withContext(Dispatchers.IO) {
val result = try {
// Make network request using a blocking call
network.fetchNextTitle().execute()
} catch (cause: Throwable) {
// If the network throws an exception, inform the caller
throw TitleRefreshError("unable to refresh title", cause)
}
if (result.isSuccessful) {
// Save it to database
titleDao.insertTitle(Title(result.body()!!))
} else {
// If it's not successful, inform the callback of the error
throw TitleRefreshError("unable to insert title", null)
}
}
修改后:
suspend fun refreshTitle() {
try {
// Make network request using a blocking call
val result = network.fetchNextTitle()
titleDao.insertTitle(Title(result))
} catch (cause: Throwable) {
// If anything throws an exception, inform the caller
throw TitleRefreshError("Unable to refresh title", cause)
}
}
分析:
(1) 依赖挂起和恢复操作会使代码大幅缩短
network.fetchNextTitle().execute() --> network.fetchNextTitle()
titleDao.insertTitle(Title(result.body()!!)) -> titleDao.insertTitle(Title(result))
且删除了 result 是否为空、是否成功的判断
(2) 去掉了 withContext。
由于 Room 和 Retrofit 都提供主线程安全挂起函数,
因此可以安全地通过 Dispatchers.Main 安排此异步工作
4. 修正编译器错误
转用协程确实涉及更改函数的签名,因为无法通过常规函数调用挂起函数。
4.1 TestingFakes.kt
(1) TitleDaoFake 添加 suspend 到 fun insertTitle()
(2) MainNetworkFake
原始:
override suspend fun fetchNextTitle() = MakeCompilerHappyForStarterCode() // TODO: replace with `result`
修改后
override suspend fun fetchNextTitle() = result // TODO: replace with `result`
修改 MainNetworkCompletableFake
原始:
override suspend fun fetchNextTitle() = MakeCompilerHappyForStarterCode() // TODO: replace with `completable.await()`
修改后
override suspend fun fetchNextTitle() = completable.await() // TODO: replace with `completable.await()`
--End--
网友评论