安卓分享

Kotlin 协程理解和实战

2022-09-06  本文已影响0人  我爱田Hebe

1.什么是协程

首先在讲协程之前,我们需要先搞清楚几个概念

1.1程序、进程、CPU、内存关系

如上图,平时我们打包好一个应用,放在磁盘上,此时我们称之为程序或者应用,是静态的,比我我们熟悉的apk文件。 当我们执行程序(比如点击某个App),OS 会将它加载进内存,CPU 从内存某个起始地址开始读取指令并执行程序。

程序从磁盘上加载到内存并被CPU运行期间,称之为进程。因此我们通常说某个应用是否还在存活,实际上说的是进程是否还在内存里;也会说某某程序CPU占用率太高,实际上说的是进程的CPU占用率。

而操作系统负责管理磁盘、内存、CPU等交互,可以说是大管家。

1.2 进程和线程之间的关系

现在我们用简单的图片展示一下他们之间的关系

上图是单核CPU的情况

1.3 线程和协程之间的关系

1.4 kotlin 协程的优势

1、协程是轻量级线程、比线程耗费资源少 这话虽然是官方说的,但我觉得有点误导的作用,协程是语言层面的东西,线程是系统层面的东西,两者没有可比性。 协程就是一段代码块,既然是代码那就离不开CPU的执行,而CPU调度的基本单位是线程。

2、协程是线程框架 协程解决了移步编程时过多回调的问题,既然是异步编程,那势必涉及到不同的线程。Kotlin 协程内部自己维护了线程池,与Java 线程池相比有些优化的地方。在使用协程过程中,无需关注线程的切换细节,只需指定想要执行的线程即可,从对线程的封装这方面来说这说话也没问题。

3、协程效率高于线程 与第一点类似,协程在运行方面的高效率其实换成回调方式也是能够达成同样的效果,实际上协程内部也是通过回调实现的,只是在编译阶段封装了回调的细节而已。因此,协程与线程没有可比性。

2.kotlin协程初认识

在Kotlin中,协程就是线程的封装,它提供了一套标准的API来帮助我们编写并发任务。

在java中实现多任务并发

//线程
new Thread(new Runnable() {
    @Override
    public void run() {
        //耗时的工作
    }
}).start();

//线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(new Runnable() {
    @Override
    public void run() {
        //耗时的工作
    }
});

android中实现多任务并发

在Android中,除了可以通过Java的方式,创建线程、使用线程池实现多任务并发之外,还可以AsyncTask等方式来实现多个耗时任务的并发执行:

//AsyncTask
public abstract class AsyncTask<Params, Progress, Result> {
  //线程池中执行,执行耗时任务
  protected abstract Result doInBackground(Params... params);
  //UI线程中执行,后台任务进度有变化则执行该方法
  protected void onProgressUpdate(Progress... values) {}
  //UI线程执行,耗时任务执行完成后,该方法会被调用,result是任务的返回值
  protected void onPostExecute(Result result) {}
}

无论是Java还是Android提供的组件,都可以实现多任务并发的执行,但是上面的组件都或多或少存在一些问题:

使用协成实现多任务并发

我们还是使用AsyncTask举例

AsyncTask<String, Integer, String> task = new AsyncTask<String, Integer, String>() {
    @Override
    protected String doInBackground(String... strings) {
        String userId = getUserId(); //获取userId
        return userId;
    }

    @Override
    protected void onPostExecute(final String userId) {
        AsyncTask<String, Integer, String> task1 = new AsyncTask<String, Integer, String>() {
            @Override
            protected String doInBackground(String... strings) {
                String name = getUserName(userId); //获取userName,需要用到userId
                return name;
            }

            @Override
            protected void onPostExecute(String name) {
                textView.setText(name); //设置到TextView控件中
            }
        };
        task1.execute(); //假设task1是一个耗时任务,去获取userName
    }
};
task.execute(); //假设task是一个耗时任务,去获取userId

如果使用kotlin协程,上面的代码可以简化如下:

GlobalScope.launch(Dispatchers.Main) {
    val userId = getUserId() //耗时任务,这里会切换到子线程
    val userName = getUserName(userId) //耗时任务,这里会切换到子线程
    textView.text = userName //设置到TextView控件中,切换到主线程
}

suspend fun getUserId(): String = withContext(Dispatchers.IO) {
    //耗时操作,返回userId
}

suspend fun getUserName(userId: String): String = withContext(Dispatchers.IO) {
    //耗时操作,返回userName
}

上面launch函数的{}的逻辑,就是一个协程。

相比于AsyncTask的写法,我们可以看到使用kotlin协程有以下好处:

3.kotlin协程的接入和使用

引入kotlin协程相关依赖

在模块的build.gradle中加入以下依赖:

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
}

使用协程

Kotlin提供了三种方式来创建协程,如下所示:

//方式一
runBlocking { //runBlocking是一个顶级函数
   ...
}

//方式二
GlobalScope.launch { //GlobalScope是一个单例对象,直接使用launch开启协程
   ...
}

//方式三
val coroutineScope = CoroutineScope(context) //使用CoroutineContext创建CoroutineScope对象,通过launch开启协程
coroutineScope.launch {
   ...
}

作者:用户202091961859
链接:https://juejin.cn/post/7139715683390554120

上一篇 下一篇

猜你喜欢

热点阅读