前言
小伙伴们,今天周六啦!好一段时间没有写过文章了,今天来一篇关于回调改写为挂起函数的文章。有的小伙伴可能要问了,为啥没有挂起函数改写成回调函数的文章呢?
因为使用 Kotlin 协程的挂起函数比使用回调方便多了,所以为什么要把挂起函数转换复杂的回调呢,这不是把天堂变成回调地狱嘛??
缘起
为啥要出这么一篇文章呢?
因为我现在在公司做的这个项目是基于一个以前的项目进行二次开发的,所以项目中就有一些使用接口回调的网络请求。然后这个项目也使用了 Kotlin ,写习惯 Kotlin 协程的我,自然而然的就不想继续写回调地狱了。于是我就打算使用协程来解决回调地狱的问题,但是由于项目中的业务逻辑实在太多了,而且业务逻辑比较复杂,导致我不敢贸然重构,所以我就从把回调改写挂起函数开始,这样既不用重写原有的逻辑,又可以享受 Kotlin 协程带来的便利,简直美汁汁儿呀??
Show your Code
账号登录信息类
/**
* @Description: 账号登录信息
* @author: anjiemo
*/
data class Account(val userName: String, val password: String)
用户基本信息类
/**
* @Description: 用户基本信息
* @author: anjiemo
*/
data class UserInfo(val userName: String, val nickName: String, val vip: Boolean)
模拟原来的业务逻辑
private val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
/**
* @Description: 模拟第一个接口,进行用户登录
* @author: anjiemo
*/
fun login(account: Account, block: (UserInfo) -> Unit) {
val startTime = System.currentTimeMillis()
println("登录,开始时间:${sdf.format(startTime)}")
// 模拟网络请求耗时操作
Thread.sleep(3_000L)
val userInfo = UserInfo(userName = account.userName, nickName = "A Lonely Cat", vip = true)
val endTime = System.currentTimeMillis()
println("登录,结束时间:${sdf.format(endTime)}")
println("耗时:${endTime - startTime} ms")
// 回调该 lambda 函数
block.invoke(userInfo)
}
/**
* @Description: 模拟第二个接口,通过用户信息获取是否可以领取津贴
* @author: anjiemo
*/
fun checkAllowances(userInfo: UserInfo, block: (Boolean) -> Unit) {
val startTime = System.currentTimeMillis()
println("检查是否有津贴,开始时间:${sdf.format(startTime)}")
// 模拟网络请求耗时操作
Thread.sleep(5_000L)
val endTime = System.currentTimeMillis()
println("检查是否有津贴,结束时间:${sdf.format(endTime)}")
println("耗时:${endTime - startTime} ms")
// 回调该 lambda 函数
block.invoke(userInfo.vip)
}
将原来的普通回调改写为挂起函数
/**
* @Description: 模拟第一个接口,进行用户登录
* 转换成支持挂起的函数
* @author: anjiemo
*/
suspend fun loginBySuspend(account: Account): UserInfo {
return suspendCoroutine { cont: Continuation<UserInfo> ->
try {
login(account) { userInfo ->
// 继续执行相应的协程,传递 [value] 作为最后一个暂停点的返回值
cont.resume(userInfo)
}
} catch (t: Throwable) {
// 恢复相应协程的执行,以便在最后一个暂停点之后立即重新抛出 [exception]
cont.resumeWithException(t)
}
}
}
/**
* @Description: 模拟第二个接口,通过用户信息获取是否可以领取津贴
* 转换成支持挂起的函数
* @author: anjiemo
*/
suspend fun checkAllowancesBySuspend(userInfo: UserInfo): Boolean {
return suspendCoroutine { cont: Continuation<Boolean> ->
try {
checkAllowances(userInfo) { hasAllowances ->
// 继续执行相应的协程,传递 [value] 作为最后一个暂停点的返回值
cont.resume(hasAllowances)
}
} catch (t: Throwable) {
// 恢复相应协程的执行,以便在最后一个暂停点之后立即重新抛出 [exception]
cont.resumeWithException(t)
}
}
}
单元测试
// 定义协程作用域
private val coroutineScope = CoroutineScope(Dispatchers.Default)
@Test
fun test() {
// 普通回调方式进行网络数据获取操作
val userAccount = Account(userName = "张三", password = "123456")
login(userAccount) { userInfo ->
checkAllowances(userInfo) { hasAllowances ->
println("函数回调:${userInfo.nickName} has hasAllowances is $hasAllowances")
}
}
println("========== 华丽的分割线 ==========")
// 此处 Activity 中可以使用 lifecycleScope,
// Fragment 中可以使用 viewLifecycleScope,
// ViewModel 中可以使用 viewModelLifecycleScope
coroutineScope.launch {
val userInfo = withContext(Dispatchers.IO) {
loginBySuspend(account = userAccount)
}
val hasAllowances = withContext(Dispatchers.IO) {
checkAllowancesBySuspend(userInfo)
}
println("协程:${userInfo.nickName} has hasAllowances is $hasAllowances")
}
// 等待协程执行完毕
Thread.sleep(10_000L)
}
测试结果
欢迎同学们点赞、评论、打赏+关注啦~
首发于阳光沙滩,允许转载至《阳光沙滩》微信公众号,其他转载请联系:2695734816@qq.com
本文由
A lonely cat
原创发布于
阳光沙滩
,未经作者授权,禁止转载