协程有两大优势:

  1. 异步逻辑同步化表达。什么意思呢,就是整个代码看下来都是同步的,只需要函数return返回结果就行,不需要考虑太多回调的方式,避免了广为所知的回调地狱
  2. 用更少的线程做尽量多的任务。从官方文档可以获取到,协程只是线程上运行的代码块,在线程阻塞的时候,使用挂起操作,使得线程可以回到线程池,继续做其他的任务,减少了很多不必要的系统资源消耗

基于第二点,我尝试过写demo进行各种验证,但是测试效果并不理想,同样的任务量(1000个局域网的网路请求——>UI渲染),运行速度,采用协程的方式好像并没有线程池快(最终都是同样的线程数量),至于资源的消耗,这个没有很明显的差别,所以就没有太多的结果和验证,关于这点,我们后续会做出更多的说明和测试验证,以及原理,也欢迎更多的朋友一起交流。今天这里,我们就先说说怎么在Android中使用kotlin 协程吧。

第一步:引入库,Android 需要引入如下两个库

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"


第二步:声明协程作用域CoroutineScope

这里建议有生命周期的类继承 CoroutineSocpe ,这样就能让全部协程跟着生命周期结束

如在activity里面使用
MainActivity : AppCompatActivity(), CoroutineScope by MainScope(){
    override fun onDestroy(){
        super.onDestory()
        cancel()
     }
}
在UI逻辑类里面使用:
class MainActivityFacede : CoroutineScope {
    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main   job

    fun destroy() {
        job.cancel()
    }
}

以上代码都会在调用 destroy 的时候取消这个作用域中的协程运行。

第三步:运行协程

前面我们建立的协程作用域,那么我们怎么运行一段协程呢?

那么我这里说两种运行协程, launch , async 。
因为我们是说快速使用,所以我这里也不跟大家扣源码了。简单说来,launch 没有返回值,或者说返回只是 job ,能够知道任务的状态,却不能携带返回结果。async 有返回值,也就是返回的是 Deferred ,它是继承的 job ,所有job有的,它都有,还具备了job没有的携带数据回来的能力。
所以, launch 可以用来运行不需要操作结果的协程(如文件删除,创建等), async 可以用来运行异步耗时任务并且需要返回值的任务(网络请求,数据库操作,文件读写等)。

简单补充一下 Job 和 Deferred 的生命周期和状态表

Android 协程原理 安卓协程使用_Android 协程原理


下面介绍一下使用方式:

方式一
private suspend fun getWebTime(): Long = withContext(Dispatchers.IO) {
    var result = RequeastTest.getInstance().start()
    val name = Thread.currentThread().name
    if (!coroutines.contains(name)) {
        coroutines.add(name)
    }
    result
}

launch() {
    //do sth
    var time = getWebTime()
    //update UI
}
launch {
    var deferred = async() {
        //发起网络请求..
        getWebTime()
    }
    //do sth ...
    var value = deferred.await()
    //do sth...
}
方式二
private suspend fun getWebTime(): Long {
    var result = RequeastTest.getInstance().start()
    val name = Thread.currentThread().name
    if (!coroutines.contains(name)) {
        coroutines.add(name)
    }
    return result
}

launch() {
    //do sth
    var time = withContext(Dispather.IO){
        getWebTime()
    }
    //update UI
    
}
launch {
    var deferred = async(Dispather.IO) {
        //发起网络请求..
        getWebTime()
    }
    //do sth ...
    var value = deferred.await()
    //do sth...
}

从上面两个方式中,我们看到了新的东西 Dispather , suspend 。我们先说说 Dispather ,我们可以理解为协程调度器,它是用来调度协程跑在哪个县城里面的,一下是取值的说明

Android 协程原理 安卓协程使用_Android_02


Dispather 可以在 launch、async 等启动协程时,指定在哪个线程里面运行,也可以在协程中,使用 withContext(Dispather.**) 来切换线程,使用 withContext 切换线程时,有返回值。

Suspend ,是协程里面唯一一个修饰符,用来修改函数的,表明函数是一个挂起函数,协程编译器会在编译期间进行CPS变换,去做一些不可描述的事情(具体怎么个不可描述的事情,我们下回再说,此处暂不详解)。用 suspend 修饰的函数,只能在协程体和同样使用 suspend 修饰的函数中调用。

以上两个的区别在于 getWebTime 的函数写法,也就造成协程的写法有所区别(调度线程的写法区别)。

上面忘了说 async 启动协程的方式的另一种使用场景。当我们需要同时进行多个网络请求,并在全部请求完毕之后进行数据整理,渲染的时候,async 比什么都方便,如下:

launch {
    var userInfoDeferred = async {
        //获取用户基本信息
        getUserInfo(aid)
    }

    var userTeamsDeferred = async{
        //获取用户团队..
        getUserTeams(aid)
    }
    
    var userOrgsDeferred =  async {
        //获取用户组织机构
        getUserOrgs(aid)
    }
    
    var userInfo = userInfoDeferred.await()
    var userTeams = userTeamsDeferred.await()
    var userOrgsDeferred = userOrgsDeferred.await()
    //渲染UI 
}

这些网络请求是同时发出的,挂起时间就是耗时最长的一个请求的请求时间,是不是很方便呀!

以上就是协程快速使用了,相信你看完这篇文章之后已经能够使用协程了,想了解更多协程原理的小伙伴们,可以关注我,我们一起交流,并且我们后续会有深入了解协程的文章!