Kotlin协程它不香吗?,全网最新

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

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

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

笔者觉得最主要的是它可以用看起来同步的方式写出异步的代码。这样写代码的人写起来很舒服,读代码的人读起来很畅快。

快速上手

下面笔者利用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,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?=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?=null
withContext(Dispatchers.IO){
//请求登录
result = repository.login(userName,userPassword).await()
}
//更新ui
btnLogin.text = result?.data?.username
}


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

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

suspend fun login(name: String,password: String): WanResponse {
return withContext(Dispatchers.IO) {
val repository = ApiRepository()
repository.login(name, password).await()
}
}


这个函数和普通函数不一样,多出来一个关键字suspend,直译过来是挂起的意思,那这个关键字真正的作用到底什么呢?这个下面会详细解释,这里先跳过。

挂起函数写好了,那开启协程部分的代码就可以改写一下

GlobalScope.launch (Dispatchers.Main){
val result =login(userName,userPassword)
btnLogin.text = result.data.username
}


这样看起来就和同步方式的代码一样了

### 还可以更简洁?

上面我们通过添加第三方依赖库来设配retrofit对kotlin协程的支持。其实是没有必要的,因为retrofit库的2.6.0版本之后就内置了对 Kotlin Coroutines 的支持,它帮我们简化了使用 Retrofit 和协程来进行网络请求的过程。

上面的代码最终可以改写成下面这个样子:

@FormUrlEncoded
@POST("/user/login")
suspend fun login(
@Field(“username”) username: String,@Field(“password”) password: String): WanResponse


可以看到只需要返回我们的WanResponse,不需要返回 Deferred<WanResponse>

suspend fun login(name: String,password: String): WanResponse{
return retrofit.login(name,password)
}


在挂起函数请求时也不需要自己调用await方法,因为retrofit已经帮我们在后面默默的调用了

suspend fun login(name: String, password: String): WanResponse {
return withContext(Dispatchers.IO) {
val repository = ApiRepository()
repository.login(name, password)
}
}

GlobalScope.launch (Dispatchers.Main){
    val result =login(userName,userPassword)
    btnLogin.text = result.data.username
} 
```

> 注意:  
> 为了方便理解,以上示例代码均没有处理异常情况,本篇博客也暂时不说,毕竟不是本文重点,而且异常处理要说详细一点可以单独再开一篇了。

### supspend 关键字的作用

上面提到了挂起函数中的suspend,那它的作用是什么呢?是挂起作用?

如果是挂起作用,那它挂起的对象是什么?是当前线程还是所在的函数?

答案是都不是,**协程中的挂起,本质上挂起的对象是协程**。协程是啥?就是launch函数包起来的代码块。

```
 GlobalScope.launch (Dispatchers.Main){
        //login是个suspend函数
        val result = login(userName,userPassword)
        btnLogin.text = result.data.username
 }
      //Next
      ..... 

suspend fun login(name: String,password: String): WanResponse {
return withContext(Dispatchers.IO) {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值