回调机制多使用匿名类来实现,但是写法过于繁琐,很多情况下每使用一次就需要重写一遍匿名类。
第一行代码中HttpURLConnection发送网络请求中使用回调返回给主线程网络请求结果的例子如下:
HttpUtil.sendHttpRequest(address, object: HttpCallbackListener{
override fun onFinish(response: String){
//得到服务器返回的具体内容
}
override fun onError(e: Exception){
//处理异常
}
})
这种写法的缺点就是,有多少网络请求就需要编写多少次的匿名类实现。
Kotlin的协程能够大大简化传统回调的写法。需要使用suspendCoroutine函数
suspendCoroutine函数必须在协程作用域或者挂起函数中才能够调用,它接收一个Lambda表达式参数,主要作用是将当前协程挂起,然后在一个普通的线程中执行Lambda表达式中的代码。
Lambda表达式的参数列表上会传入一个continuation参数,调用它的reusme()方法或reusmeWithException()可以让协程恢复执行。
使用协程简化后的回调写法如下:
suspend fun request(address: String): String {
return suspendCoroutine{ continuation ->
HttpUtil.sendHttpRequest(address, object : HttpCallbackListener{
override fun onFinish(response: String) {
continuation.resume(response)
}
override fun onError(e: Exception){
continuation.resumeWithException(e)
}
}
}
}
request函数是一个挂起函数,函数内部调用了suspendCoroutine函数,这样当前协程就会立刻被挂起,Lambda表达式中的代码则会在普通线程中执行。如果http请求成功,就调用Continuation的resume()方法恢复被挂起的协程,并传入服务器响应的数据,该值就会成为suspendCoroutine函数的返回值。如果请求失败,就调用Continuation的resumeWithException)恢复被挂起的协程,并传入具体的异常原因。
example:
使用retrofit进行网络请求写法(简化前)
val appService = ServiceCreator.create<AppService>()
appService.getAppData().enqueue(object : Callback<App> {
override fun onResponse(call: Call<List<App>>, response: Response<List<App>>){
//得到服务器返回的数据
}
override fun onFailure(call: Call<List<App>>, t: Trowable){
//处理异常
}
}
简化后写法:
suspend fun <T> Call<T>.await(): T{
return suspendCoroutine{ continuation ->
enqueue(object : Callback<T>{
override fun onResponse(call: Call<T>, response: Response<T>){
val body = response.body
if(body != null) {
continuation.resume(body)
}else {
continuation.resumeWithException(RuntimeException(
"response body is null"))
}
}
override fun onFailure(call: Call<T>, t: Throwable){
continuation.resumeWithException(RuntimeException(t))
}
})
}
}
suspend fun getAppData() {
try {
val appList = ServiceCreator.create<AppService>().getAppData().await()
//对服务器响应的数据进行处理
}catch (e: Exception) {
//对异常情况进行处理
}
}
参考:《第一行代码android》