Kotlin31 协程如何与 Java 进行混编?

简介: Kotlin31 协程如何与 Java 进行混编?

本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

Kotlin31 协程如何与 Java 进行混编?

问题

在 Java 与 Kotlin 混编项目中大概率是会遇到 Kotlin 线程的使用问题。协程的混编相对于其他特性的使用上会相对麻烦而且比较容易踩坑。我们以获取 token 来举例,比如有一个获取 token 的 suspend 函数:

kotlin 代码解读复制代码// 常规的 suspend 函数,可以供 Kotlin 使用,Java 无法直接使用
suspend fun getTokenSuspend(): String {
    // do something too long
    return "Token"
}

想要在 Java 中直接调用则会产出如下错误:

image.png

了解 Kotlin 协程机制的同学应该知道 suspend 修饰符在 Kotlin 编译器期会被处理成对应的 Continuation 类,这里不展开讨论。

这个问题也可以使用简单的方式进行解决,那就是使用 runBlocking 进行简单包装一下即可。

使用 runBlocking 解决

一般情况下我们可能会使用以下代码解决上述问题。定义的 Kotlin 协程代码如下:

kotlin 代码解读复制代码// 提供给 Java 使用的封装函数,Java 代码可以直接使用
fun getTokenBlocking(): String =runBlocking{// invoke suspend fun
    getTokenSuspend()
}

在 Java 层代码的使用方式大致如下:

typescript 代码解读复制代码public void funInJava() {
   
    String token = TokenKt.getTokenBlocking();
}

看上去方案比较简单,但是直接使用 runBlocking 也会存在一些隐患。 runBlocking 会阻塞当前调用者的线程,如果是在主线程进行调用的话,会导致 App 卡顿,严重的会导致 ANR 问题。那有没有比 runBlocking 更合理的解决方案呐?

回答这个问题之前,先梳理下 Java 与 Kotlin 两种语言在处理耗时函数的一般做法。

Java & Kotlin 耗时函数的一般定义

Java

  • 靠语义约束。比如定义的函数名中 sync 修饰,表明他可能是一个耗时的函数,更好的还会添加 @WorkerThread 注解,让 lint 帮助使用者去做一些检查,确保不会在主线程中去调用一些耗时函数导致页面卡顿。
  • 靠语法约束,定义 Callback。将耗时的函数执行放到一个单独的线程中执行,然后将回调的结果通过 Callback 的形式返回。这种方式无论调用者是什么水平,代码质量都不会有问题;

Kotlin

  • 靠语义约束,同 Java
  • 添加 suspend 修饰,靠语法约束。内部耗时函数切到子线程中执行。外部调用者使用同步的方式调用耗时函数却不会阻塞主线程(这也是 Kotlin 协程主要宣传的点)。

在 Java 与 Kotlin 混编的项目中,上述情况的复杂度将会上升。

使用 CompletableFuture 解决

在审视一下 runBlocking 的使用问题,这种做法是将 Kotlin 中的语法约束退化到语义约束层面了,有的可能连语义层面的约束都没有,这种情况只能祈求调用者的使用是正确的 -- 在子线程调用,而不是在主线程调用。那应该如何怎么处理,就是采用回调的方式,让语法能够规避的问题就不要采用语义来处理。

kotlin 代码解读复制代码suspend fun getToken(): String {
    // do something too long
    return "Token"
}

fun getTokenFuture(): CompletableFuture<String>  {
    returnCoroutineScope(Dispatchers.IO).future{getToken()}
}

注意:future 是 org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 包中提供的工具类,基于 CoroutineScope 定义的扩展函数,使用时需要导入依赖包。

Java 中的使用方式如下:

csharp 代码解读复制代码public void funInJava() {
   
    try {
   
        // 通过 Future get() 显示调用 getTokenFuture 函数
        TestKt.getTokenFuture().get();
    } catch (ExecutionException e) {
   
        e.printStackTrace();
    } catch (InterruptedException e) {
   
        e.printStackTrace();
    }
}

可能会问这里看上去和直接在函数内部使用 runBlocking 没有太大的区别,反而使用上会更麻烦些。的确是这样的,这样的目的是把选择权交给调用者,或者说让调用者显示的知道这不是一个简单的函数,从而提高其在使用 API 时的警惕度,也就是之前提到的从语法层面对 API 进行约束。

退一步说,上述的内容是针对的仅仅是“不得不”这么做的场景,但是对于大部分场景都是可以通过合理的设计来避免出现上述情况:

  • 底层定义的 suspend 函数可以在上层的 ViewModel 中的 viewModelScope 中调用解决;
  • 统一对外暴露的 API 是 Java 类的话,新增的 API 提供可以使用 suspend 类型的扩展函数,使用 suspend 类型对外暴露;
  • 如果明确知道调用者是 Java 代码,那么请提供 Callback 的 API 定义;

总结

尽量使用合理的设计来尽量规避 Kotlin 协程与 Java 混用的情况,在 API 的定义上语法约束优先与语义约束,语义约束优于没有任何约束。当然在特殊的情况下也可以使用 CompletableFuture API 来封装协程相关 API。

下面对几种常见场景推荐的一些写法:

  1. 在单元测试中可以直接使用 runBlocking
  2. 耗时函数可以直接定义为 suspend 函数或者使用 Callback 形式返回;
  3. 对于 Java 类中调用协程函数的场景应使用显示的声明告知调用者,严格一点的可以判断线程,对于在主线程调用的可以抛出异常或者记录下来统一处理;
相关文章
|
1月前
|
Java 开发工具 Android开发
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
本系列教程笔记详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。若需快速学习Kotlin,建议查看“简洁”系列教程。本期重点介绍了Kotlin与Java的共存方式,包括属性、单例对象、默认参数方法、包方法、扩展方法以及内部类和成员的互操作性。通过这些内容,帮助你在项目中更好地结合使用这两种语言。
45 1
|
3天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
14 2
|
1月前
|
Java 编译器 Android开发
Kotlin语法笔记(28) -Kotlin 与 Java 混编
本系列教程详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。对于希望快速学习Kotlin的用户,推荐查看“简洁”系列教程。本文档重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android library开发建议以及在Kotlin和Java之间互相调用的方法。
24 1
|
8天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)
17 0
|
1月前
|
安全 Java 编译器
Kotlin语法笔记(27) -Kotlin 与 Java 共存(二)
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。若需快速入门,建议查阅“简洁”系列教程。本文重点探讨Kotlin与Java共存的高级话题,包括属性访问、空安全、泛型处理、同步机制及SAM转换等,助你在项目中逐步引入Kotlin。
26 1
|
1月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
|
6月前
|
Java Android开发 C++
Kotlin vs Java:选择最佳语言进行安卓开发
【4月更文挑战第13天】Java曾是安卓开发的主流语言,但Kotlin的崛起改变了这一局面。Google在2017年支持Kotlin,引发两者优劣讨论。Java以其成熟稳定、强大生态和跨平台能力占优,但代码冗长、开发效率低和语言特性过时是短板。Kotlin则以简洁语法、空安全设计和高度兼容Java脱颖而出,但社区和生态系统仍在发展中,可能存在学习曲线和性能问题。选择语言应考虑项目需求、团队熟悉度、维护性、性能和生态系统。无论选择哪种,理解其差异并适应新技术至关重要。
344 4
|
Java 开发者 Kotlin
Kotlin为什么会火起来,有什么特点,跟Java区别
Kotlin为什么会火起来,有什么特点,跟Java区别
103 0
|
安全 Java 开发者
Kotlin 和 Java对比,具体代码分析
@[TOC](目录) Kotlin 和 Java 都是广泛使用的编程语言,它们有一些共同点,例如都追求面向对象编程,但也有许多不同之处。下面是 Kotlin 和 Java 之间的一些比较: # 一、语法比较 1. 声明变量:Kotlin 使用 val 和 var 关键字来声明不可变和可变变量,而 Java 使用 final 和非 final 修饰符。 2. 类型推断:Kotlin 支持类型推断,可以省略变量类型,而 Java 必须显式指定变量类型。 3. 可空类型:Kotlin 使用?和!! 操作符来表示可空类型和非空类型,而 Java 没有这样的区分。 4. 函数声明:Kotlin 使用 f
168 0
|
Java Android开发 Kotlin
kotlin查看编译后的Java代码
kotlin查看编译后的Java代码
下一篇
无影云桌面