【KMP踩坑记录】Parcelize插件没法用了?

背景

KMP跨平台是一门新兴的技术,在此时入局未免会碰到各式各样的坑,而本系列文章将记录我使用 KMP 以来所踩的坑,作为记录,同时也帮助大家入局 KMP。

爬坑

一、K2编译器 + Parcelize + KMP 无法正常使用

背景:看到这个问题,大家肯定很疑惑,Parcelable 是Android 平台的特性,但是为什么需要在 KMP 上使用呢?

事情是这样的,Compose Multiplatform 作为一个从 Android 平台中迁移的 UI 框架,不可避免地会使用到 Android 的特性,像是 rememberSaveable默认情况下在Android 中的实现就是将内容存到 SavableStateRegistry中,并在 UI复原时从中读取复原。

如果我们不特地声明一个Saver,存基础数据例如 Int、Long、String当然没有问题,但是如果是一个数据类型,会自动将它作为一个 Parcelable对象存储。但是问题来了,common模块并没有 Parcelable这一个概念。

那能不能声明一个 common 模块 Parcelable,它在 Android 平台上就正常实现,其他平台就空实现呢?答案是可以的。

kotlin 复制代码
// common package
expect interface CommonParcelable

@OptIn(ExperimentalMultiplatform::class)
@OptionalExpectation
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
expect annotation class CommonParcelize()

// android package
actual typealias CommonParcelable = android.os.Parcelable

// For parcelaze plugin
actual typealias CommonParcelize = kotlinx.parcelize.Parcelize

编写以上代码之后我们就可以对我们 common 模块的数据类使用了:

kotlin 复制代码
@CommonParcelize
@Stable
data class NoteItem(
    val id: Long,
    val title: String,
    val content: String
): CommonParcelable

// use it
var noteItems: List<NoteItem> by rememberSaveable {
    mutableStateOf(emptyList())
}

就这么简单几行代码就可以正常运行。使用默认的Saver把数据类当 Parcelable 存储而不用自己手动自定义 Saver真的很方便。

但是,这种写法在 K2 编译器无法使用了。

Kotlin 编译器有两个模块,其中一个是 Kotlin 语法模块,用于理解编写的代码,另一个是编译模块,用于将 Kotlin 代码生成特定平台可执行的产物。例如 Bytecode,JavaScript,Executable,Wasm。他们也称为 frontend 和 backend。

K2 Compiler 重写了 frontend,将编译次数降为一次,使编译拥有更优的性能,然而在编译期无法访问平台特性的代码,例如平台代码的类型、注解等等。

这将直接导致了 Parcelize 插件在使用 K2 的 Kotlin Multiplatform 项目不可用,由于 Parcelize 插件是 Android 平台特供的,需要检测 Android 平台代码的注解并生成代码,而 KMP 的 common 代码是不与平台相关的,这将导致现存的 expect/actual 标识的注解将无法在 K2 compilier 上使用。

而目前Parcelize插件官方的解决方式是增加编译参数,触发平台代码检测,从而使特定注解和其使用的地方增加一次平台代码检测,使 Parcelize 插件生效。

kotlin 复制代码
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=example.CommonParcelize")

而这种方式也是有副作用的,增加了一次平台代码检测,也增加了编译时间。这也算是编译时间换编码时间。

二、 Gradle二次混淆问题

gradle 8.5 以上的 library module 需要将 isMinifyEnabled 设置为 false,否则会造成多次混淆,即:App module将代码混淆一遍,Library 也将代码混淆一遍,最直接的现象是 release 包跑起来找不到类而闪退。

三、KMP Desktop找不到Class

在面向 Desktop 平台的产物中,我们会带一个JDK 到程序中。

一个 Hello World 程序直接达到了 200MB

而问题就出在了这个JDK,由于Gradle 无法自动检测你运行时实际上需要使用到哪些类,打进去的 JDK 是有删减的,当某个类没有打包进程序中,在运行时将会报错。

所以可以在打包前检测一下自己需要哪些类,又或者报错时通过错误信息查看哪些类丢失。在 Gradle 脚本中声明:

kotlin 复制代码
nativeDistributions {
    modules("java.sql")
}

当然,如果你懒,又或者你并不在意包体大小,可以直接声明把整个 JDK 打进包里,避免运行时异常。

kotlin 复制代码
nativeDistributions {
    includeAllModules = true
}

后记

由于新技术的的资料较少,部分内容未必准确,欢迎勘误。当然,Kotlin 迭代非常快,当你看到这篇文时,可能某些缺陷已经被修复,请以最新的为准。

参考

github.com/JetBrains/c...

kotlinlang.org/docs/whatsn...

issuetracker.google.com/issues/3157...

相关推荐
geovindu2 小时前
css: Timeline scribble
前端·css·iphone
沙漠之主10 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
hasty10 小时前
不上传新包,也能改变用户拿到的版本:npm dist-tag 的 OIDC 权限治理
前端·npm·node.js
Csvn10 小时前
diff 算法(虚拟 DOM Reconciliation)
前端
前端snow11 小时前
ai agent --- 文件存储
前端
Frag0ut11 小时前
Chrome四大版本获取及共存指南:Stable/Beta/Dev/Canary
前端·chrome·浏览器·dev·beta·canary·共存版
ZealSinger13 小时前
Boot4挂起函数丢traceId怎么修
spring boot·kotlin·协程·可观测性
IT_陈寒13 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
广州华水科技13 小时前
单北斗GNSS变形监测在大坝安全监测中的应用与优势
前端
zeng不错13 小时前
B站 173 个投稿活动看到眼瞎?我写了个扩展,主打一个精准打击
前端