Kotlin开发Android App和Java的差异7----Kotlin中使用协程执行并发操作

个人淘宝店铺需要的小伙伴可以点进来

1 依赖配置

如果在Android项目中使用协程,需要配置以下依赖

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")

2 实现协程的方式

在Kotlin中,实现协程的方式有很多,常见的方式包括async、lunch、withContext

其中,launch不存在返回值,async可以存在返回值,两者都不会阻塞主线程

2.1 launch的方式创建协程

Kotlin在Android中,通过launch创建协程比较常见,在官方文档中也是通过launch来举例子

数据接口

interface IWeatherDataSource {

    suspend fun getCurrentWeather(): Alarm
}

其中suspend代表了当前函数是一个耗时方法,只能在协程中或者其他suspend函数中调用,当协程中碰到suspend关键字,将会把协程挂起,不会阻塞线程

Repository

suspend fun getCurrentAlarm():Alarm{

    return iWeatherDataSource.getCurrentWeather()
    
	//2.保证主线程安全
	return withContext(Dispatchers.IO){
        return@withContext iWeatherDataSource.getCurrentWeather()
    }
}

Kotlin在Android存在多种协程Scope用来创建协程,例如ViewModel中的viewModelScope,或者Activity / Fragment中的lifecycleScope

fun getCurrentWeather() : MutableLiveData<Alarm>{

    val result:MutableLiveData<Alarm> = MutableLiveData<Alarm>()

    viewModelScope.launch {

        var currentAlarm = repository.getCurrentAlarm()
        result.postValue(currentAlarm)
    }
    return result
}

2.2 async的方式创建协程

通过async的方式创建的协程具备返回值,不需要像launch那样自定义一个返回值,需要配合await来取出返回值

suspend fun getCurrentWeather() : MutableLiveData<Alarm>{

    var result =  viewModelScope.async(Dispatchers.IO){

        var currentAlarm = repository.getCurrentAlarm()
        return@async MutableLiveData(currentAlarm)
    }

    return result.await()

}

2.3 保证主线程安全

使用suspend函数不会让kotlin在后台线程上运行,在主线程上运行suspend函数也很常见,因此如果为了保证主线程的安全,所有的suspend函数中都要使用withContext,保证了耗时操作一定是在IO或者Default线程中运行

suspend fun getCurrentAlarm():Alarm{
    
	//2.保证主线程安全
	return withContext(Dispatchers.IO){
        return@withContext iWeatherDataSource.getCurrentWeather()
    }
}

因为withContext是一个暂停函数,所以getCurrentAlarm也是一个暂停函数,这样在调用getCurrentAlarm的时候,就会挂起协程,等到withContext执行完成之后,恢复

3 协程中的异常处理

在Kotlin协程中,同样可以使用try-catch代码块包裹可能出现异常的代码块,但是和Java不同的时,个别情况下,try-catch代码能够捕获异常,但是app会崩溃,有的情况下则不会崩溃

3.1 supervisorScope 和 coroutineScope的使用方式

viewModelScope.launch {

   try {

       coroutineScope {

           var getUserError =  async { NewsDataSource.RetrofitBuilder.api.getUsersWithError().await() }

           var getUsers = async { NewsDataSource.RetrofitBuilder.api.getUsers().await() }


           var userErrorFromApi = try {

               getUserError.await()

           }catch (e:Exception){
               emptyList()
           }

           var userFromApi = try {

               getUsers.await()

           }catch (e:Exception){

               emptyList()

           }
//
           userList.postValue(userFromApi)
       }

   }catch (e:Exception){

       Log.e("TAG",e.message.toString())
   }
}

在coroutineScope协程块中,有两个异步处理,其中getUserError是会报错的(Http 404),这是异常的,通过try-catch能够捕获这个异常,另一个是正常的;

如果按照Java的理解,捕获到了异常并不会使得app崩溃,但是coroutineScope就是不管哪个异步方式报错,都会停止协程,而且app崩溃。

而使用supervisorScope协程块,虽然getUserError会报错,但是并不会影响第二个协程的执行,而且正常上传数据更新UI。

3.2 async 和 launch 抛出异常的区别

使用launch发生异常就会立即抛出,因此需要使用tyr-catch代码块包裹launch中可能会出现异常的代码;

viewModelScope.launch {

    try {

        var users1 = repository.getUsersWithError().await()
        users.postValue(users1)

    }catch (e:Exception){

        users.postValue(emptyList())
    }
}

使用async不会立刻抛出异常,而是在调用await的时候会抛出异常,因此需要将await使用try-catch代码包裹

viewModelScope.launch {

var dd =  async { repository.getUsers().await() }

try {

     var await = dd.await()

     users.postValue(await)

 }catch (e:Exception){

     users.postValue(emptyList())

 }
}

这种情况下,App会崩溃?为什么,官方文档中声明,async只有在await的时候,才会抛出异常吗,为什么已经捕获了异常,但是app还是崩溃了?

主要原因就是,async作为根协程的时候,才会在await的时候,抛出异常;其他情况下,很有可能在async中也抛出异常。

viewModelScope.launch(exeptionHandler) {

    try {

        var dd =  async { repository.getUsersWithError().await() }

        var await = dd.await()

        users.postValue(await)

    }catch (e:Exception){

        users.postValue(emptyList())

    }
}

这种将async也包裹起来,就能够阻止app崩溃;或者将根协程由launch改为async

viewModelScope.async(exeptionHandler) {

    var dd =  async { repository.getUsersWithError().await() }

    try {

        var await = dd.await()

        users.postValue(await)

    }catch (e:Exception){

        users.postValue(emptyList())

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Awesome_lay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值