目录:
1)协程是什么?
2)协程和线程的关系?
3)协程如何使用?切线程是什么
4)挂起函数是什么?
5)withContext和lanuch的区别在哪里?
6)Defualt、IO和Main的区别
7)lifecycleScope
一、协程是什么?
协程就是用线程来实现的并发管理库,简单来说就是对线程的封装。那么他的作用是什么?
下面我们来看看一个例子。
以前java
Thread {
//进行耗时操作:比如Http操作
var user = api.getUser()
runOnUiThread {
tvName.text = user.getName()
//处理后,切换到UI线程,将数据显示出来。
}
}.start()
现在协程
CoroutineScope(Dispatchers.Main).launch {
var user = api.getUser()
tvName.text = user.getName()
}
以前我们要进行一些耗时操作,需要另外起来一个线程,线程处理后,如果要将数据回显到UI,还需要使用方法切换的UI线程。然而,如果使用了协程,这些步骤全部都被封装了起来,我们可以用串行的方式写并行的代码。
二、协程和线程的关系
通过下面这张图,我们可以看到,协程是在线程里面的,就像线程和进程的关系一样。
三、协程如何使用?切线程是什么
为什么要切线程:因为我要执行耗时操作。
切线程一般两种:
- 切到后台线程或子线程。
- 切到UI线程。
那么如何切换呢?
(1)这就是一个切换协程的方法(launch )。从任意线程切换到了IO协程。
CoroutineScope(Dispatchers.IO).launch {
...
}
(2)使用withContext 方法切换协程
val coroutineScope = CoroutineScope(Dispatchers.Main)
coroutineScope.launch(Dispatchers.Main) { // 在 UI 线程开始
withContext(Dispatchers.IO) { // 切换到 IO 线程
//getImage(imageId) // 在 IO 线程执行
}
textView.text = "asdfasdfasdf" // 回到 UI 线程更新 UI
}
// 在UI线程上执行操作
CoroutineScope(Dispatchers.Main).launch {
// 更新UI组件
textView.text = "Hello,!"
}
// 在IO线程上执行网络请求
CoroutineScope(Dispatchers.IO).launch {
val result = performNetworkRequest()
withContext(Dispatchers.Main) {
// 在UI线程上更新UI组件
textView.text = result
}
}
// 在默认的调度器上执行计算密集型任务
CoroutineScope(Dispatchers.Default).launch {
val result = performComplexCalculation()
withContext(Dispatchers.Main) {
// 在UI线程上更新UI组
textView.text = result.toString()
}
}
四、挂起函数是什么
挂起函数是什么?
他其实就是暂停的意思。就是他执行这个函数的时候,协程就被挂起,协程函数给到其他线程执行,所在的协程就暂停了,执行完后,挂起函数后面的代码才会执行。
挂起的本质就是,协程和线程脱离,暂时不占用线程了,你可以去做别的事情了。协程在其他线程执行完,再切回来。
挂起函数必须在协程里面才能使用,不然挂起谁。
下面我们来看一个例子。
(1)比如我们现在需要发起一个请求
interface HomeApiService {
/**
* 获取首页Banner数据
*
* @return BaseResponse<List<BannerBean>> Banner列表
*/
@GET("banner/json")
suspend fun getBanners(): BaseResponse<List<BannerBean>> //suspend: 表示这是一个挂起函数,可以在协程中调用,并可以在执行过程中挂起。
/**
* 获取置顶文章
* @return BaseResponse<MutableList<ArticleBean>> 置顶文章列表
*/
@GET("article/top/json")
suspend fun getTopArticle(): BaseResponse<MutableList<ArticleBean>>
/**
* 根据页面获取分页的文章数据
* @param page Int 获取的文章页码 从 0 开始
* @return BaseResponse<ArticlePageBean>
*/
@GET("article/list/{page}/json")
suspend fun getArticleByPage(@Path("page") page: Int): BaseResponse<ArticlePageBean>
}
(2)发起请求
val coroutineScope = CoroutineScope(Dispatchers.Main)
coroutineScope.launch(Dispatchers.IO) {
var result = HomeApiService.getBanners()
showResult(result)
}
HomeApiService.getBanners()这里会挂起,等结果返回后,执行showResult方法。
五、withContext和lanuch的区别
lanuch开启的是并行的切线程,withContext则是串行,也就是withContext其实也是一个挂起函数,后面的代码会等待前面的代码执行完成。而lanuch则不会。
六、Defualt、IO和Main的区别
- io:跟内存之外的世界去交互【读写磁盘,以及进行HTTP请求之类的网络访问】
- main:是Android应用程序的主线程调度器,也称为UI线程。它用于执行与用户面相关的操作,例如更新UI组件、处理用户输入等。
- Default:这是一个适用于CPU集型任务的调度器。它使用一个线程池来执行并发的计算密集型任务,例如排序、解析大型数据等。Dispatchers.Default通常比Dispatchers.IO更适合执行需要较长时间的计算任务但仍建议将长时间运行的任务放在后台线程中,以避免阻塞UI线程。
- main.immediate:main.immediate和main线程的区别就是,如果已经在主线程就不需要使用handler.post抛到主线程,而是直接执行。
七、lifecycleScope
lifecycleScope,是什么?直接再activity里面了。可以直接调用。
他的特点:
- 他的生命周期和activity绑定,如果是fragment,则是fragment【意思是如果你正在http请求,那么他也会直接结束?对!!!】