Android开发:使用Kotlin协程(Coroutine)和Retrofit执行网络和取消请求
演示了如何使用Coroutine配合Retrofit
来触发Android
中如何优雅地取消一个已经使用协程时启动网络请求。
本文的演示标题:
创建协程作用域
在上一篇文章中,我写到方法是一个非常常用的协程构建器。因此,要使用协程,首先必须创建一个对象
CoroutineScope
。代码如下:
CoroutineScope( + Job())
复制代码
上面的代码创建了一个CoroutineScope
对象,并指定其协程在主线程上执行。 ,并且分配了Job
。在演示中,我使用了MVP模式来编写,所以我把CoroutineScope
的创建放在BasePresenter
中。代码如下:
interfaceMvpViewinterfaceMvpPresenter<V: MvpView> {
@UiThreadfunattachView(view: V)@UiThreadfundetachView()
}
openclassBasePresenter<V: MvpView> : MvpPresenter<V> {
lateinitvar view: V
val presenterScope: CoroutineScope by lazy {
CoroutineScope( + Job())
}
overridefunattachView(view: V) {
this.view = view
}
overridefundetachView() {
()
}
}
复制代码
使用()取消协程
你应该看到上面中调用了
()
。那么这个方法的作用是什么呢?它是关于取消 presenterScope
所有创建的协程及其子协程。
在上一篇文章中,我也介绍了launch
在创建协程时的使用,Job
对象将通过Job
对象返回cancel方法也可以取消任务对应的协程,那我这里为什么不使用那个方法呢?
显然,如果使用
()
方法取消协程,那么我创建每个协程后,都需要保存返回的Job
对象,然后取消它。显然如果想要更复杂的话,使用()
一次性取消协程上下文创建的所有协程和子协程。该代码也可以轻松地提取到基类中,以便您稍后编写。编写业务代码时,无需担心协程和视图的生命周期。
其实看源码也可以发现,最后的
()
也是用来取消协程
的,可以看上面。 api接口定义大家应该很熟悉了。可以通过以下代码发起异步网络请求().enqueue(object : Callback<T> {
overridefunonFailure(call: Call<T>, t: Throwable) {
}
overridefunonResponse(call: Call<T>, response: Response<T>) {
}
})
复制代码
().enqueue(object : Callback<T> {
overridefunonFailure(call: Call<T>, t: Throwable) {
}
overridefunonResponse(call: Call<T>, response: Response<T>) {
}
})
复制代码
上一篇文章介绍了协程可以让异步代码像编写同步代码一样方便。上面的异步代码可以使用协同程序吗?将其更改为编写同步代码块之类的内容怎么样?这显然是可能的。具体修改代码如下:
suspend fun<T> Call<T>.await(): T {
return suspendCoroutine {
enqueue(object : Callback<T> {
overridefunonFailure(call: Call<T>, t: Throwable) {
(t)
}
overridefunonResponse(call: Call<T>, response: Response<T>) {
if() {
(()!!)
} else{
(Throwable(()))
}
}
})
}
}
复制代码
上述代码扩展了hang函数
await
。执行该方法时,会执行一个异步请求,同时协程序会被挂起。运行此函数直到异步请求失败或发生错误,然后继续例程。
suspendCoroutine
全局函数,该函数可以获取当前方法所在协程的上下文并挂起当前协程,直到一定时间再恢复协程执行,但是这个时间其实是由开发者自己控制的,仅作为上面代码中的
和
。
发起请求,写为
{
val time = ()
()
try {
val ganks = queryGanks()
(ganks)
} catch (e: Throwable) {
()
} finally {
(TAG, "耗时:${() - time}")
}
}
suspend funqueryGanks(): List<Gank> {
returntry {
val androidResult = ().await()
val iosResult = ().await()
val result = mutableListOf<Gank>().apply {
addAll()
addAll()
}
result
} catch (e: Throwable) {
()
throw e
}
}
复制代码
从上面的代码中可以看到,协程使用了
try-catch
方法来处理异常。首先,这就是我到目前为止所想到的。方式。因此,在使用协程时,最好在异常捕获企业中适当的地方使用try-catch
。否则,当例程执行过程中出现异常时,程序就会崩溃。
另外,上面代码的写法还有一个问题。由于执行临时函数时当前协程会暂停,上面的两个请求是顺序执行的,所以上面的方法
queryGanks()
实际上需要两个网络请求,因为两个android列表请求和ios列表请求它们不是并行的,所以这种写法肯定不是最优方案。
触发请求,写入方法2
我们换一种写入方法吧。
suspend funqueryGanks(): List<Gank> {
return withContext() {
try {
val androidDeferred = async {
val androidResult = ().await()
androidResult
}
val iosDeferred = async {
val iosResult = ().await()
iosResult
}
val androidResult = ().results
val iosResult = ().results
val result = mutableListOf<Gank>().apply {
addAll(iosResult)
addAll(androidResult)
}
result
} catch (e: Throwable) {
()
throw e
}
}
}
复制代码
这种写法与之前的区别在于
async
构造函数创建了两个子例程,分别请求Android列表和IOS列表。同时,由于async
构建器,当前协程在执行过程中不会被挂起,所以两个请求都是并行执行的,所以效率比之前的写法要高很多。
提交请求,第三种写法
第三种写法是从
Retorfit
打乱CallAdapter
,通过自定义CallAdapter来实现工厂
,将api。定义Call
的结果直接转换为Deferred
,这样Android列表请求和IOS列表就可以同时触发,然后通过获取请求结果。这种写法是第一种写法两种写法的结合。
这个写法
Jake Wharton
师父已经给我们实现了。地址在这里...
这里就不讲这个方案的具体实现了。有兴趣的同学可以查看其源码。
第三种写法的特殊代码如下:
val instance = ()
.baseUrl("")
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(())
.build().create(CallAdapterApiService::class.java)
suspend funqueryGanks(): List<Gank> {
return withContext() {
try {
val androidDeferred = ()
val iosDeferred = ()
val androidResult = ().results
val iosResult = ().results
val result = mutableListOf<Gank>().apply {
addAll(iosResult)
addAll(androidResult)
}
result
} catch (e: Throwable) {
()
throw e
}
}
}
复制代码
上面的第三种写法看起来更简洁,也是并行请求。取请求时间最长的请求时间,与第二种方法类似。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。