协程?我们明确一下这篇文章,我们需要了解什么东西.
1.协程是什么东西
2.协程用来干什么
3.协程怎么写
> 4.你对我的爱有多深
根据大佬们的理解,协程大概可以理解为.
一种轻量级线程,协程类似于线程,但是它算是一种可以在单线程模式下模拟多线程编程的效果
.里面状态中的挂起和恢复和我们的操作系统无关,只和函数有关
区别
:线程运行在内核态,协程运行在用户态
总结:Android kotlin中协程是一个线程框架
根据定义得出作用:跟线程差不多的作用, 用来做异步/网络请求
如果运行在Android上,请先引入依赖:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
(1).GlobalScope.launch: 顶层协程域,任何地方都可以开始创建,但当应用程序结束时,协程也会跟着一起结束。(Android不常用)
很容易看出,下面的语句中,只有"initDebug 1"被打印,而"initDebug 2"因为程序已经结束而无法被打印出.
这里解释一下delay()函数,这里是让协程延迟指定时间后再运行,是协程特有的。
fun main(){
GlobalScope.launch {
Log.d(TAG, "initDebug: 1")
delay(1500)
Log.d(TAG, "initDebug: 2")
}
Thread.sleep(1000)
}
(2)runBlocking: 协程域.但它可以保证在协程域内所有代码和子协程在没有执行完前一直阻塞当前线程. (相当不常用)
会发现,两条语句都被打印出来了.
fun main(){
runBlocking {
Log.d(TAG, "initDebug: 1")
delay(1500)
Log.d(TAG, "initDebug: 2")
}
Thread.sleep(1000)
}
(3)coroutineScope:作用域。是一个挂起函数,它的作用就是继承外部协程作用于并创建一个子作用域,一般用作在挂起函数内,开启一个子协程域
(常用在于挂起函数内)
解释一下挂起函数,简单理解就是要求这个函数必须执行在协程中的函数
suspend fun oo(){
coroutineScope {
launch {
}
}
(4).viewModel.viewModelScope.launch: 配合ViewModel使用上的,一个常用分配作用域,因其跟随着Activity/Fragment的生命周期 (非常推荐使用).
看其源码,你们可以看到CoroutineScope类
,它返回的就是这么一个对象,这个对象可以使用.lanuch()去随意创建一个成协程
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
这里可以看到, 这里返回的是Job,这里可以通过cancle(),让协程进入cancle()状态,注意,是cancle状态,而不是直接取消掉这个协程,后面会说到这个点
val Job = viewModel.viewModelScope.launch {
oo()
}
Job.cancle()
(1).launch: 开启一个协程,返回一个Job对象.
我们这里只用在Androdi上最常用创建协程域的方法进行操作
我们可以来分析一下这里,这里相当于一个父协程内开启了另一个子协程
val Job = viewModel.viewModelScope.launch {
val JobChildren =launch {
Log.d(TAG, "Job1: ")
}
}
(2).async: 开启一个线程,返回Deferred对象。会返回代码块内执行的结果.使用await(),获取结果
但它有一个更重要的特性,若代码块内还未执行完毕,await()方法获取结果时会将当前协程阻塞
val Job = viewModel.viewModelScope.launch {
val result =async {
Log.d(TAG, "Time:${System.currentTimeMillis()}")
//模拟网络请求数据
delay(1000)
5+5
}.await()
Log.d(TAG, "等待获取结果:${result}")
Log.d(TAG, "Time:${System.currentTimeMillis()}")
}
我们这里可以看到,我们在花时间请求数据后,协程会等待返回结果再继续执行下去.可以看到,这里可以说解决了我们痛恨的死亡 回调地狱的问题
.
D/NemoMainActivity: Time:1639619877149
D/NemoMainActivity: 等待获取结果:10
Time:1639619878152
有了调度器之后,协程才真正意义上被赋予了灵魂.
这里实现一个功能。
我们知道一般的网络请求,我们会放到异步去执行, 执行完毕后,才回到主线程进行UI操作,那么我们的协程是如何实现的呢?
就是那么简单粗暴,我们可以在开启协程的时,添加线程参数.
Dispatchers.Default:默认的低并发线程策略。
Dispatchers.Main:Android主线程内执行
Dispatchers.IO:高并发线程策略.
val Job = viewModel.viewModelScope.launch {
val result =async(Dispatchers.IO) {
Log.d(TAG, "Time:${System.currentTimeMillis()}")
//模拟网络请求数据
delay(1000)
5+5
}.await()
launch(Dispatchers.IO) {
binding.TestButton2.text = result
}
}
withContext(),这里提醒一个withContext,它类似于async(),但是这里强制要求我们指定一个线程参数。
val Job = viewModel.viewModelScope.launch {
val result =withContext(Dispatchers.IO) {
Log.d(TAG, "Time:${System.currentTimeMillis()}")
//模拟网络请求数据
delay(1000)
5+5
}.await()
launch(Dispatchers.IO) {
binding.TestButton2.text = result
}
}
根据上面我们所说,我们应该很容易想到了协程的好处,以及我们应用的地方.
(1)网络请求,可以随意切换线程去触发网络请求或者调整UI.并且防止了回调地狱.
让写异步代码,读起来像同步一样
(2)并发请求:我们可以在同一个协程域下,开启多个协程,每个协程都去进行网络请求.实现了并发请求~
当然,这里只是最普通的用法,它还可以用来很多很多的地方,与Flow的配合,再加上网络请求框架okhttp+retrofit.就是如虎添翼了.
具体可以看一下我之前的文章
https://blog.csdn.net/qq_33902817/article/details/120290711
另外这里提醒一下,协程使用的是状态管理.如果你要在一个持续任务中cancle掉协程,而延时任务还在进行.cancle是无法直接取消协程的.具体看这个帖子.
https://www.jianshu.com/p/42152d53349a
kotlin的协程还是需要我们一个个去掌握的…当然,有一些业务情况下,RXJAVA还是蛮好用的,比如间隔任务之类的.具体任务具体分析嘛~
不过这个是让我不要忘了以及搞混淆kotlin协程之间的关系…我经常搞混0-0~
项目常用的建立实例:
1.使用ViewModel,不阐述了.一般用这个.
2.基本用法
//-------------------------较好的实例-------------------------//
suspend fun GoodDemo1(dispatcher: CoroutineDispatcher){
withContext(dispatcher) { }
}
fun GoodDemo2(){
val job =Job()
val coroutineScope = CoroutineScope(job).launch{
}
}
//-------------------------较好的实例-------------------------//