共计 2283 个字符,预计需要花费 6 分钟才能阅读完成。
本文次要介绍如何在 ViewModel 封装通用的网络申请,不过在真正介绍封装前先解说下应用到的基础知识:协程中异样的捕捉
1. 协程中异样的捕捉
协程中异样捕捉的形式有两种:
- 常见的
try-catch
相比拟于CoroutineExceptionHandler
,这种形式能够灵便的捕获可能产生异样的代码块:
lifecycleScope.launch {
// 省略其余逻辑代码
try {val result = 8 / 0} catch (e: Exception) { }
// 省略其余逻辑代码
}
- 协程上下文元素
CoroutineExceptionHandler
这种形式捕获的异样是间接捕获的整个协程块的异样,颗粒度比拟大,短少灵活性。
lifecycleScope.launch(CoroutineExceptionHandler { _, throwable ->
Log.e("ChapterActivity", "exception occur: ${throwable.message}")
}) {
// 省略其余逻辑代码
val result = 8 / 0
// 省略其余逻辑代码
}
2. 封装网络申请
首先定义一个类,类中别离定义三种函数类型属性,别离用作:发动申请、申请胜利、申请失败
。
class Action<T> {
// 发动申请
var request: (suspend () -> Response<T>)? = null
private set
// 申请胜利
var success: ((T) -> Unit)? = null
private set
// 申请失败
var error: ((Throwable) -> Unit)? = null
private set
fun request(block: suspend () -> Response<T>) {request = block}
fun success(block: (T) -> Unit) {success = block}
fun error(block: (Throwable) -> Unit) {error = block}
}
data class Response<T>(val code: Int = -1, val data: T? = null)
定义一个扩大函数 netRequest
,以DSL
的形式创立 Action<T>
对象:
fun <T> ViewModel.netRequest(block: Action<T>.() -> Unit) {val action = Action<T>().apply(block)
}
咱们就能够这样创立 Action<T>
对象并为其函数类型的属性别离设置值:
netRequest<String> {
request {
// 模仿执行网络耗时
delay(5* 1000)
Response("dd")
}
success {// 申请胜利执行代码逻辑}
error {// 申请失败执行的代码逻辑}
}
架子基本上咱们曾经搭建起来了,在解决网络申请之前先定义一个异样,专门用于解决申请响应失败的问题。
data class CustomException(val code: Int = -1, val throwable: String? = null) : Exception(throwable)
当初就得在 netRequest
办法中利用 协程
来执行网络申请、解决申请后果回调以及异样的捕捉:
fun <T> ViewModel.netRequest(block: Action<T>.() -> Unit) {val action = Action<T>().apply(block)
viewModelScope.launch(CoroutineExceptionHandler { _, throwable ->
action.error?.invoke(throwable)
}) {val result = withContext(Dispatchers.IO) {action.request!!.invoke()
}
if (result.code == 0) {action.success?.invoke(result.data)
} else {action.error?.invoke(CustomException(result.code, "申请失败"))
}
}
}
通过 viewModelScope
开启一个协程,不过咱们并没有间接在外层的 launch
就间接指定执行的线程,而是通过 withContext
指定散发器 Dispatchers.IO
去独自执行耗时申请,申请完结后会主动将线程从 withContext
指定的线程中切换到外层 launch
协程块所在的线程。
如果间接在外层 launch
间接指定 Dispatchers.IO
,这就会导致申请胜利action.success
和申请失败 action.error
的执行的环境为非主线程,防止后续开发者再额定解决这种非主线程回调的状况。
通过上述一步步流程,咱们就最终搭建胜利了一个简单网络申请工具类。
搭配 Retrofit
提供的 suspend
适配器
com.squareup.retrofit2:retrofit:2.9.0
这个版本的 retrofit
曾经适配了协程的suspend
:
判断是否为 suspend 润饰的申请办法:suspend
润饰办法会在办法参数上减少一个 Continuation
参数:#RequestFactory.java
在 HttpServiceMethod
中:
具体的源码就不带大家看了,要害就在于 SuspendForBody
办法中的 adapt
办法中,外部会调用 OkhttpClient#Call
的办法 enqueue
开启线程去执行网络申请,执行结束之后会再切换到调用申请的地位所在的线程,感兴趣的请自行查看。