赞
踩
优点:写法很简单,轻量级,挂起几乎不消耗内存,速度上优于java的线程,性能损耗小,能大幅度提高并发性能,本人推荐使用协程,而不用传统的线程
Kotlin 提供了三个调度程序,以用于指定应在何处运行协程:
Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。此调度程序只能用于与界面交互和执行快速工作。示例包括调用 suspend 函数,运行 Android界面框架操作,以及更新LiveData对象。
Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络I/O。示例包括使用Room组件、从文件中读取数据或向文件中写入数据,以及运行任何网络操作。
Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'//lifecycleScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'//viewModelScope
延时delay(1000L)
CoroutineScope(Dispatchers.IO).launch {
//delay(1000L)
Log.d("wang","===3====")
}
也可以在方法里这么写
fun main() = runBlocking {
launch {
delay(2000)
Log.d("wang","==2==")
}
Log.d("wang","==3==")
}
代码:
suspend fun fetchDocs() {
val result = get("https://developer.android.com") // get方法里延迟一秒返回结果
showUI(result.toString()) //此处会等上一行代码异步结束了才运行
}
/**
* 模拟网络请求
*/
suspend fun get(url: String) = withContext(Dispatchers.IO) {
delay(1000L)
Log.d("WANG", "===>get结果")
}
/**
* 更新UI
*/
fun showUI(result: String) {
Log.d("WANG", "showUI===>$result")
}
调用:
CoroutineScope(Dispatchers.IO).launch {
fetchDocs()
}
1、异步登录
suspend fun login(name:String,pass:String):LoginBean = suspendCancellableCoroutine { ctn->
HttpMethods.webService.login(name,pass){
override fun onSuccess(bean: LoginBean) {
ctn.resume(bean) //协程恢复,返回结果
}
override fun onError(e: Exception) {
ctn.resumeWithException(e) //协程恢复,抛出异常
}
}
2、异步使用token请求
suspend fun connect(token:String):LoginBean = suspendCancellableCoroutine { ctn->
HttpMethods.webService.connect(token){
override fun onSuccess(bean: LoginBean) {
ctn.resume(bean) //协程恢复,返回结果
}
override fun onError(e: Exception) {
ctn.resumeWithException(e) //协程恢复,抛出异常
}
}
3、调用
launch{
val bean = login(name,pass) //在获取结果之前,协程是挂起状态不会执行下一步
val result = connect(bean.token)
}
fun main() = runBlocking<Unit> {
val one = async { doOne() }
val two = async { doTwo() }
Log.e("TAG","测试运行") //此代码会立即运行,如果要等待异步结果返回才运行,需要添加await
println("==两个异步相加结果为: ${one.await() + two.await()}")
}
suspend fun doOne(): Int {
delay(1000L) // 假设我们在这里做了些有用的事
return 13
}
suspend fun doTwo(): Int {
delay(2000L) // 假设我们在这里也做了些有用的事
return 29
}
调用:main()
suspend fun getSynPermissions(userPermissList: MutableList<UserPermissBean>): Boolean {
val list = userPermissList.map { it ->
async {
CabinetRepository.getInstance().getSynPermissions(it, errMsg)
}
}
val results = list.awaitAll()
return results.all { it }
}
LifecycleScope 在 AppCompatActivity 和 Fragment 中使用已经十分方便了,不过,目前很多项目都开始使用 MVVM,更多逻辑操作都是在 ViewModel 层,这时,就需要用到 ViewModelScope 了,它会自动绑定 ViewModel 的生命周期。
若需要使用 ViewModelScope,我们需要添加依赖:
注意:
1、对于Dispatchers.IO 以下调用会报错:
lifecycleScope.launch(Dispatchers.IO) { // 这里报错:Only the original thread that created a view hierarchy can touch its views. testView.text = "测试" }
- 1
- 2
- 3
因为这在IO线程中来更新UI了。
修复方法可以尝试这样:// 这里报错:Only the original thread that created a view hierarchy can touch its views. withContext(Dispatchers.Main) { testView.text = "测试" } }
- 1
- 2
- 3
- 4
2、CoroutineScope是什么?如果你觉得陌生,那么GlobalScope、lifecycleScope与viewModelScope相信就很熟悉了吧(当然这个是针对于Android开发者)。它们都实现了CoroutineScope接口。
±--------------+
3、在Android中使用协程来请求数据,当接口还没有请求完成时Activity就已经退出了,这时如果不停止正在运行的协程将会造成不可预期的后果。所以在Activity中我们都推荐使用lifecycleScope来启动协程,lifecycleScope可以让协程具有与Activity一样的生命周期意识。
MainActivity中调用
lifecycleScope.launch(Dispatchers.IO) {
val result = mainViewModel.getInfo11()
Log.e("WY", "收到结果:$result")
}
viewmodel中代码
suspend fun getInfo11():Boolean = suspendCancellableCoroutine { ctn->
viewModelScope.launch {
delay(20000) // 这里模拟网络请求
Log.e("WY", "getInfo11===1")
ctn.resume(true){}
// ctn.resumeWithException(Throwable("会崩溃")) // 抛异常
}
Log.e("WY", "==getInfo11===2")
// ctn.cancel() // 调用cancel,调用getInfo11的地方不会往下执行,即不会调用Log.e("WY", "收到结果:$result")
}
这里viewModelScope会跟随生命周期的取消而取消
方法:在viewmodel里面的生命周期onCleared里加日志,activity按系统返回键后销毁会调用onDestroy,onCleared也会跟着调用,这时候看getInfo11方法里Log.e(“WY”, “getInfo11=1")不会输出,则说明viewModelScope协程是否跟随生命周期销毁而销毁,如果viewModelScope.launch换成一般的协程,日志Log.e(“WY”, "getInfo11=1”)是会输出
override fun onCleared() {
super.onCleared()
Log.e("WY", "onCleared释放掉viewmodel")
}
如果在activity或fragment中我们可以使用:lifecycleScope和viewmodelScope,这样不会造成内存泄漏,但是如果不在呢,应该怎么正确使用协程?
方式一:如果你的协程不在 Activity 或 Fragment 中使用,那么你可以创建一个全局的协程作用域来管理它们。通常情况下,这个全局协程作用域应该用单例对象来实现。
以下是一个示例代码,使用协程单例来进行网络请求:
object NetworkManager {
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
suspend fun fetchSomeData(): String {
return withContext(coroutineScope.coroutineContext) {
// 这里是协程代码,进行网络请求
"这是请求到的数据"
}
}
}
方式二:在 ViewModel 中使用协程
在 ViewModel 中使用协程可以帮助你在后台执行长时间运行的任务,而不会阻塞 UI 线程。
在 ViewModel 中使用协程需要引入 kotlinx.coroutines 依赖并调用 viewModelScope.launch 函数。
通过使用 viewModelScope.launch 函数,你可以在协程作用域内执行异步操作,比如网络请求或者数据库操作。
private fun run5() {
//1、不阻塞主线程(推荐)
CoroutineScope(Dispatchers.IO).launch {
//执行代码.....
}
//2、优秀的线程切换
CoroutineScope(Dispatchers.Main).launch {
val task1 = withContext(Dispatchers.IO) {
Log.d("LUO","1111========${DateTimeHelper.format(Date(),"yyyy-MM-dd HH:mm:ss")}")
delay(2000)
"服务器返回值:json" //服务器返回结果赋值给task1
}
//刷新UI,task1
Log.d("LUO","2222========${DateTimeHelper.format(Date(),"yyyy-MM-dd HH:mm:ss")}")
Log.d("LUO", "值===========${task1}")
}
Log.d("LUO","3333========${DateTimeHelper.format(Date(),"yyyy-MM-dd HH:mm:ss")}")
}
***方式三:在 协程结束的时候进行释放 ***
val stopHandShake = CoroutineScope(Job())
stopHandShake.launch {
try {
} catch (e: Exception) {
}
}.invokeOnCompletion {
stopHandShake.cancel()
}
我们开发时候经常遇到这样的一个需求:**秒后超时,并中断执行的任务
fun main() = runBlocking<Unit> {
launch {
val result1 = withTimeoutOrNull(7000L) { //7秒后超时,并且中断执行的任务
val time = (Random().nextInt(10 - 1) + 1).toLong() //生成1到10随机数
// 模仿网络请求,结果:成功,失败,超时
delay(time * 1000) // 模仿耗时
val netResult = (Random().nextInt(10 - 1) + 1).toLong() // 随机数如果大于5则成功,小于5则失败
if (netResult > 5) ArrayList<Int>() else 1//这里可以返回任意的类型
}
println("执行返回的结果为: $result1") // 结果:超时(null),失败(1),成功ArrayList<Int>()
}
}
引入
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18"
创建一个协程
launch(CommonPool) {
//这里需要运行的代码
}
协程里面嵌套协程
launch(CommonPool) {
//创建带返回值的协程async
var job = async(CommonPool) {
delay(5000L)
return@async "nihao"
}
var job1 = launch(CommonPool) {
delay(5000L)
return@async "nihao2"
}
}
协程的取消:
job1.cancel()
launch(CommonPool) { // 在一个公共线程池中创建一个协程
delay(1000L) // 非阻塞的延迟一秒(默认单位是毫秒)
//延时一秒后执行
}
有关协程的挂起,恢复,调度这里就不做过多的介绍!!
new Handler().postDelayed(new Runnable() { @Override public void run() { // 延时执行的代码 } }, 2000);
以上代码不会导致内存泄漏。因为在postDelayed()方法中传递的Runnable对象是匿名内部类,它不会持有外部类的引用。当延迟时间到达时,Runnable对象会被Handler处理并执行,然后被垃圾回收器回收。因此,不会发生内存泄漏。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。