写点什么

Kotlin 协程它不香吗?,kotlin 开发游戏

用户头像
Android架构
关注
发布于: 刚刚

asyncFunc1(opt, (...args1) {


asyncFunc2(opt, (...args2) {


asyncFunc3(opt, (...args3) {


asyncFunc4(opt, (...args4) {


// some operation


});


});


});


});


看起来是不是有点恶心,如果你没有感觉到恶心,笔者觉得你可能经常写这样的代码所以习惯了,有一句“名人”名言:吐着吐着就习惯了。


到这里优秀的你肯定又想到了 Rxjava 这把利器,我们可以通过它提供的「Observable」的编程范式进行链式调用,可以很好地消除回调。


那么这里介绍的协程到底可以做什么呢?上面的问题它自然是可以解决了,那它相较于 RxJava 的优势是什么呢?


笔者觉得最主要的是它可以用看起来同步的方式写出异步的代码。这


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


样写代码的人写起来很舒服,读代码的人读起来很畅快。

快速上手

下面笔者利用 Retrofit 配合协程实现一个登录功能


首先需要添加以下依赖库


implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"


implementation 'com.squareup.retrofit2:retrofit:2.6.2'


implementation 'com.squareup.retrofit2:converter-gson:2.6.0'


//为 Retrofit 添加对 Deferred 的支持


implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'


根据 wanandroid 的登录 API 接口,通过 retrofit 框架敲出客户端的登录接口


接口 API 链接: www.wanandroid.com/blog/show/2


interface ApiService {


companion object {


const val BASE_URL = "https://www.wanandroid.com"


}


@FormUrlEncoded


@POST("/user/login")


fun login(@Field("username") username: String,


@Field("password") password: String): Deferred<WanResponse<User>>


}


Deferred 是什么?它是 Job 的子接口。那,,,Job 又是什么呢?可以简单理解,整个登录请求的过程就是会被封装成 Job,然后交给协程调度器处理。但 Job 在完成的时候是没有返回值的,所以就有了 Deferred,它的意思就是延迟,结果稍后才能拿到,它可以为任务完成时提供返回值。


根据请求后返回的 json,写出返回值的数据类


data class WanResponse<out T>(val errorCode: Int,val errorMsg: String,val data: T)


data class User(val collectIds: List<Int>,val email: String,


val icon: String,val id: Int,


val password: String, val type: Int, val username: String)


之后构建一个 retrofit 实例,用它来进行请求登录


class ApiRepository {


val retrofit = Retrofit.Builder()


.baseUrl(ApiService.BASE_URL)


.addConverterFactory(GsonConverterFactory.create())


//添加对 Deffered 的支持


.addCallAdapterFactory(CoroutineCallAdapterFactory.invoke())


.build()


.create(ApiService::class.java)


fun login(name: String,password: String): Deferred<WanResponse<User>>{


return retrofit.login(name,password)


}


}


接下来主角协程要出场了。我们可以通过 launch 函数开启一个协程


GlobalScope.launch(Dispatchers.IO) {


var result: WanResponse<User>?=null


result = repository.login(userName,userPassword).await()


launch(Dispatchers.Main) {


btnLogin.text = result.data.username


}


}


这段代码出现了 Dispatchers 调度器,它可以将协程限制在一个特定的线程执行,或者将它分派到一个线程池,或者让它不受限制地运行,关于 Dispatchers 这里先不展开了。


常用的 Dispatchers ,有以下三种:



  • Dispatchers.Main:Android 中的主线程


  • Dispatchers.IO:针对磁盘和网络 IO 进行了优化,适合 IO 密集型的任务,比如:读写文件,操作数据库以及网络请求


  • Dispatchers.Default:适合 CPU 密集型的任务,比如计算


但上面的栗子只是一次网络请求,如果有多次请求可能就变成这个样子:


GlobalScope.launch(Dispachers.IO) {


//io 操作


launch(Dispachers.Main){


//ui 操作


launch(Dispachers.IO) {


//io 操作


launch(Dispacher.Main) {


//ui 操作


}


}


}


}


这个嵌套???不是说协程可以不用写嵌套代码的吗


于是协程中有一个很实用的函数:withContext这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行,


用 withContext 改写一下,它的结构大致就长这个亚子:


launch(Dispachers.Main) {


...


withContext(Dispachers.IO) {


...


}


...


withContext(Dispachers.IO) {


...


}


...


}


复制代码


比如上面的登录的栗子就可以改写成这样:


GlobalScope.launch(Dispatchers.Main) {


var result: WanResponse<User>?=null


withContext(Dispatchers.IO){


//请求登录


result = repository.login(userName,userPassword).await()


}


//更新 ui


btnLogin.text = result?.data?.username


}


好像的确变得简洁了许多,但离我们的目标:看起来同步的方式写出异步的代码还差那么一点。


既然不需要嵌套了,那就可以把 io 线程的操作,拿出来单独作为函数,就可以写成这样:

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Kotlin协程它不香吗?,kotlin开发游戏