IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin的协程:挂起函数 -> 正文阅读

[移动开发]Kotlin的协程:挂起函数

挂起函数

挂起函数是指使用 suspend 关键字修饰的函数。

suspend fun getUserInfo(): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "BoyCoder"
}

挂起和恢复

挂起函数与普通函数的区别在于:挂起函数可以挂起和恢复。挂起和恢复也是协程与线程相比的优势。

考虑下面一种场景:

  1. 获取用户信息
  2. 获取用户的好友
  3. 获取每位好友的动态

如果使用 Java,可能会这么写:

public class SuspendFunction {

    public void getAllFeeds() {
        getUserInfo(new Callback() {
            @Override
            public void onSuccess(String user) {
                if (user != null) {
                    System.out.println(user);
                    getFriendList(user, new Callback() {
                        @Override
                        public void onSuccess(String friendList) {
                            if (friendList != null) {
                                System.out.println(friendList);
                                getFeedList(friendList, new Callback() {
                                    @Override
                                    public void onSuccess(String feedList) {
                                        if (feedList != null) {
                                            System.out.println(feedList);
                                        }
                                    }
                                });
                            }
                        }
                    });
                }
            }
        });
    }

    private void getFeedList(String friendList, Callback callback) {
        new Thread(() -> {
            SystemClock.sleep(1000L);
            if (callback != null) {
                callback.onSuccess("feedList");
            }
        }).start();
    }

    private void getFriendList(String user, Callback callback) {
        new Thread(() -> {
            SystemClock.sleep(1000L);
            if (callback != null) {
                callback.onSuccess("friendList");
            }
        }).start();
    }

    private void getUserInfo(Callback callback) {
        new Thread(() -> {
            SystemClock.sleep(1000L);
            if (callback != null) {
                callback.onSuccess("user");
            }
        }).start();
    }

    public interface Callback {
        void onSuccess(String response);
    }
}

这种多重回调的模式被称为“回调地狱”,代码嵌套层次多,可读性差。

如果使用 Kotlin 的挂起函数改写,会变得很简单:

fun main() = runBlocking {
    val userInfo = getUserInfo()
    val friendList = getFriendList(userInfo)
    val feedList = getFeedList(friendList)
    println(feedList)
}

suspend fun getUserInfo(): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "BoyCoder"
}

suspend fun getFriendList(user: String): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "friendList"
}

suspend fun getFeedList(friendList: String): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "feedList"
}

挂起函数的特点是使用同步的方式完成异步任务。

以下面的代码为例

val userInfo = getUserInfo()

getUserInfo 使用 withContext 切换到 IO 线程,延迟 1 秒,然后返回结果。程序在调用 getUserInfo 时挂起,然后返回结果给 userInfo 时恢复。不需要像 Java 使用回调来传递结果。

等号 “=” 右边的代码执行在子线程并挂起,右边执行完毕后,等号 “=” 左边的代码恢复到主线程执行。

深入理解 suspend

挂起函数的本质就是 Callback。Kotlin 的编译器会将 suspend 函数转换为带有 Callback 的普通函数。

反编译之前的 Kotlin 代码,可以看出 getUserInfo 挂起函数转换为了带有 Continuation 参数的普通函数。

@Nullable
public static final Object getUserInfo(@NotNull Continuation var0) {
    ...
}

Continuation 的定义如下:

public interface Continuation<in T> {
    /**
     * The context of the coroutine that corresponds to this continuation.
     */
    public val context: CoroutineContext

    /**
     * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
     * return value of the last suspension point.
     */
    public fun resumeWith(result: Result<T>)
}

Continuation 是一个接口,带有 resumeWith 方法用来返回结果。本质和 Callback 的作用是一样的。

这种将 suspend 函数转换为带有 Continuation 普通函数的过程叫做 CPS 转换

CPS:Continuation-Passing-Style Transformation。

CPS转换就是将程序接下来要执行的代码进行传递的一种模式,它将原本的同步挂起函数转换为 Callback 异步代码。

协程之所以是非阻塞,是因为它支持“挂起和恢复”,而挂起和恢复的能力,主要来自挂起函数。挂起函数是由 CPS 实现的,其中的 Continuation,本质上是 Callback。

协程与挂起函数

协程的主要能力来自挂起函数,但是协程不等同于挂起函数。

挂起函数只能在协程或者其他挂起函数中调用。因为只有协程和其他挂起函数能够提供 Continuation。

从 runBlocking 的参数 block 可以看出,block 也是一个挂起函数。

public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {

因为挂起函数能够调用挂起函数,协程提供了 block 挂起函数,所以协程也能调用挂起函数。

挂起和恢复是协程的一种底层能力,而这种能力的实现,依靠的是挂起函数。

小结

  1. 挂起函数使用 suspend 关键字表示。
  2. 挂起函数能够以同步的方式写异步代码。
  3. 挂起函数拥有挂起和恢复的能力。
  4. 挂起函数的本质是 Callback,也就是 Continuation,Kotlin 编译器会完成 CPS 转换。
  5. 挂起函数只能在协程或者其他挂起函数中调用。
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:15:26  更:2022-05-08 08:16:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 23:23:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码