志在指尖
用双手敲打未来

怎么正确的在 Android 上使用协程 ?

你还记得是哪一年的GoogleIO正式宣告Kotlin成为Android一级开发言语吗?是GoogleIO2017。现在两年时刻过去了,站在一名Android开发者的角度来看,Kotlin的生态环境越来越好了,相关的开源项目和学习材料也日渐丰厚,身边愿意去运用或许试用Kotlin的朋友也变多了。终年混迹掘金的我也能显着感觉到Kotlin标签下的文章渐渐变多了(其实依然少的可怜)。本年的GoogleIO也放出了KotlinFirst的口号,许多新的API和功用特性将优先供给Kotlin支持。所以,时至今日,真实找不到安卓开发者不学Kotlin的理由了。
今天想聊聊的是KotlinCoroutine。虽然在Kotlin发布之初就有了协程,可是直到2018年的KotlinConf大会上,JetBrain发布了Kotlin1.3RC,这才带来了稳定版的协程。即便稳定版的协程现已发布了一年之余,可是如同并没有足够多的用户,至少在我看来是这样。在我学习协程的各个阶段中,遇到问题都鲜有地方能够求助,抛到技能群根本就石沉大海了。根本只能靠一些英文文档来解决问题。
关于协程的文章我看过很多,总结一下,无非下面几类。
第一类是Medium上抢手文章的翻译,其实我也翻译过:
在Android上运用协程(一):GettingTheBackground
在Android上运用协程(二):Gettingstarted
在Android上运用协程(三):RealWork
说实话,这三篇文章的确加深了我对协程的理解。Android
第二类便是官方文档的翻译了,我看过至少不下于五个翻译版本,仍是觉得看官网文档比较好,假如英文看着真实吃力,能够对照着Kotlin中文站的翻译来阅览。
在看完官方文档的很长一段时刻,我简直只知道GlobalScope。的确,官方文档上根本从头到尾都是在用GlobalScope写示例代码。所以一部分开发者,也包含我自己,在写自己的代码时也就直接GlobalScope了。一次偶然的时机才发现其实这样的问题是很大的。在Android中,一般是不主张直接运用GlobalScope的。那么,在Android中应该如何正确运用协程呢?再细分一点,如何直接在Activity中运用呢?如何合作ViewModel、LiveData、LifeCycle等运用呢?我会经过简略的示例代码来论述Android上的协程运用,你也能够跟着着手敲一敲。
协程在Android上的运用
GlobalScope
在一般的运用场景下,咱们都期望能够异步进行耗时任务,比方网络恳求,数据处理等等。当咱们脱离当前页面的时分,也期望能够撤销正在进行的异步任务。这两点,也正是运用协程中所需求注意的。已然不主张直接运用GlobalScope,咱们就先试验一下运用它会是什么效果。
privatefunlaunchFromGlobalScope(){
GlobalScope.launch(Dispatchers.Main){
valdeferred=async(Dispatchers.IO){//networkrequestdelay(3000)”Getit”}
globalScope.text=deferred.await()
Toast.makeText(applicationContext,”GlobalScope”,Toast.LENGTH_SHORT).show()
}
}
在launchFromGlobalScope()办法中,我直接经过GlobalScope.launch()发动一个协程,delay(3000)模仿网络恳求,三秒后,会弹出一个Toast提示。运用上是没有任何问题的,能够正常的弹出Toast。可是当你履行这个办法之后,立即按回来键回来上一页面,依然会弹出Toast。假如是实际开发中经过网络恳求更新页面的话,当用户现已不在这个页面了,就根本没有必要再去恳求了,只会浪费资源。GlobalScope显然并不契合这一特性。Kotlin文档中其实也详细说明晰,如下所示:
Globalscopeisusedtolaunchtop-levelcoroutineswhichareoperatingonthewholeapplicationlifetimeandarenotcancelledprematurely.AnotheruseoftheglobalscopeisoperatorsrunninginDispatchers.Unconfined,whichdon’thaveanyjobassociatedwiththem.
Applicationcodeusuallyshoulduseanapplication-definedCoroutineScope.UsingasyncorlaunchontheinstanceofGlobalScopeishighlydiscouraged.
大致意思是,Globalscope通常用于发动顶级协程,这些协程在整个运用程序生命周期内运转,不会被过早地被撤销。程序代码通常应该运用自界说的协程效果域。直接运用GlobalScope的async或许launch办法是强烈不主张的。
GlobalScope创立的协程没有父协程,GlobalScope通常也不与任何生命周期组件绑定。除非手动办理,否则很难满意咱们实际开发中的需求。所以,GlobalScope能不必就尽量不必。
MainScope
官方文档中提到要运用自界说的协程效果域,当然,Kotlin现已给咱们供给了适宜的协程效果域MainScope。看一下MainScope的界说:
publicfunMainScope():CoroutineScope=ContextScope(SupervisorJob()+Dispatchers.Main)
记取这个界说,在后面ViewModel的协程运用中也会借鉴这种写法。
给咱们的Activity完结自己的协程效果域:
classBasicCorotineActivity:AppCompatActivity(),CoroutineScopebyMainScope(){}
经过扩展函数launch()能够直接在主线程中发动协程,示例代码如下:
privatefunlaunchFromMainScope(){
launch{
valdeferred=async(Dispatchers.IO){//networkrequestdelay(3000)”Getit”}
mainScope.text=deferred.await()
Toast.makeText(applicationContext,”MainScope”,Toast.LENGTH_SHORT).show()
}
}
最终别忘了在onDestroy()中撤销协程,经过扩展函数cancel()来完结:
overridefunonDestroy(){super.onDestroy()cancel()
}
现在来测验一下launchFromMainScope()办法吧!你会发现这完全契合你的需求。实际开发中能够把MainScope整合到BaseActivity中,就不需求重复书写模板代码了。
ViewModelScope
假如你运用了MVVM架构,根本就不会在Activity上书写任何逻辑代码,更甭说发动协程了。这个时分大部分作业就要交给ViewModel了。那么如安在ViewModel中界说协程效果域呢?还记得上面MainScope()的界说吗?没错,搬过来直接运用就能够了。
classViewModelOne:ViewModel(){
privatevalviewModelJob=SupervisorJob()
privatevaluiScope=CoroutineScope(Dispatchers.Main+viewModelJob)
valmMessage:MutableLiveData<String>=MutableLiveData()
fungetMessage(message:String){
uiScope.launch{
valdeferred=async(Dispatchers.IO){
delay(2000)
“post$message”
}
mMessage.value=deferred.await()
}
}
overridefunonCleared(){
super.onCleared()
viewModelJob.cancel()
}
}
这儿的uiScope其实就等同于MainScope。调用getMessage()办法和之前的launchFromMainScope()效果也是一样的,记得在ViewModel的onCleared()回调里撤销协程。
你能够界说一个BaseViewModel来处理这些逻辑,防止重复书写模板代码。然而Kotlin便是要让你做相同的事,写更少的代码,于是viewmodel-ktx来了。看到ktx,你就应该理解它是来简化你的代码的。引进如下依靠:
implementation”androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha03″
然后,什么都不需求做,直接运用协程效果域viewModelScope就能够了。viewModelScope是ViewModel的一个扩展特点,界说如下:
valViewModel.viewModelScope:CoroutineScopeget(){valscope:CoroutineScope?=this.getTag(JOB_KEY)if(scope!=null){returnscope
}returnsetTagIfAbsent(JOB_KEY,CloseableCoroutineScope(SupervisorJob()+Dispatchers.Main))
}
看下代码你就应该理解了,仍是熟悉的那一套。当ViewModel.onCleared()被调用的时分,viewModelScope会主动撤销效果域内的一切协程。运用示例如下:
fungetMessageByViewModel(){
viewModelScope.launch{
valdeferred=async(Dispatchers.IO){getMessage(“ViewModelKtx”)}
mMessage.value=deferred.await()
}
}
写到这儿,viewModelScope是能满意需求的最简写法了。实际上,写完全篇,viewModelScope依然是我认为的最好的选择。
LiveData
Kotlin相同为LiveData赋予了直接运用协程的能力。增加如下依靠:
implementation”androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03″
直接在liveData{}代码块中调用需求异步履行的挂起函数,并调用emit()函数发送处理结果。示例代码如下所示:
valmResult:LiveData<String>=liveData{
valstring=getMessage(“LiveDataKtx”)
emit(string)
}
你可能会好奇这儿如同并没有任何的显现调用,那么,liveData代码块是在什么履行的呢?当LiveData进入active状况时,liveData{}会主动履行。当LiveData进入inactive状况时,经过一个可装备的timeout之后会主动撤销。假如它在完结之前就撤销了,当LiveData再次active的时分会从头运转。假如上一次运转成功结束了,就不会再从头运转。也便是说只有主动撤销的liveData{}能够从头运转。其他原因(比方CancelationException)导致的撤销也不会从头运转。
所以livedata-ktx的运用是有一定约束的。对于需求用户主动改写的场景,就无法满意了。在一次完好的生命周期内,一旦成功履行完结一次,就没有办法再触发了。这句话不知道对不对,我个人是这么理解的。因此,仍是viewmodel-ktx的适用性更广,可控性也更好。
LifecycleScope
implementation”androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha03″
lifecycle-runtime-ktx给每个LifeCycle对象经过扩展特点界说了协程效果域lifecycleScope。你能够经过lifecycle.coroutineScope或许lifecycleOwner.lifecycleScope进行访问。示例代码如下:
fungetMessageByLifeCycle(lifecycleOwner:LifecycleOwner){
lifecycleOwner.lifecycleScope.launch{
valdeferred=async(Dispatchers.IO){getMessage(“LifeCycleKtx”)}
mMessage.value=deferred.await()
}
}
当LifeCycle回调onDestroy()时,协程效果域lifecycleScope会主动撤销。在Activity/Fragment等生命周期组件中咱们能够很方便的运用,可是在MVVM中又不会过多的在View层进行逻辑处理,viewModelScope根本就能够满意ViewModel中的需求了,lifecycleScope也显得有点那么食之无味。可是他有一个特别的用法:
suspendfunLifecycle.whenCreated()
suspendfunLifecycle.whenStarted()
suspendfunLifecycle.whenResumed()
suspendfunLifecycleOwner.whenCreated()
suspendfunLifecycleOwner.whenStarted()
suspendfunLifecycleOwner.whenResumed()
能够指定至少在特定的生命周期之后再履行挂起函数,能够进一步减轻View层的担负。
总结
以上简略的介绍了在Android中合理运用协程的一些方案,示例代码已上传至Github。关于MVVM+协程的实战项目,能够看看我的开源项目wanandroid,同时也等待你宝贵的定见。

未经允许不得转载:IT技术网站 » 怎么正确的在 Android 上使用协程 ?
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载