您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
京东攻克业界难题: KMP 组件交付从大仓模式到分仓模式
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
京东攻克业界难题: KMP 组件交付从大仓模式到分仓模式
he****
2026-09-23
IP归属:北京
58浏览
## 背景与现状 我们用 KMP 复用 Android、iOS 和鸿蒙的业务逻辑。目前,各组件分别维护源码仓库、依赖和版本,再打包成各平台可用的库,供 App 工程集成。 改造前的交付方式(业界通用方案): * **Android**:通过 AAR / Gradle Module 交付,由 Gradle 管理依赖。 * **iOS**:将全部 KMP 业务模块打进一个 Umbrella Framework(聚合 Framework)。 * **鸿蒙**:将全部 KMP 业务模块聚合打包为一个 HAR。 iOS 和鸿蒙各自的聚合包中,业务模块共用一份 Kotlin 标准库(stdlib)和 Native runtime(原生运行时)。修改任一组件,都要重新生成、发布整个聚合包。源码和版本已按组件管理,iOS、鸿蒙的交付包仍是一个整体。  *图 1 聚合模式:所有业务模块随同一个产物交付* 如果每个组件都编译出一份包含全部依赖的 Native 库,stdlib、runtime 和部分依赖代码就会重复出现。拆分时需要确定:哪些代码由组件自己生成,哪些放入共用的基础库。文中将这些基础库称为“公共基座”。 ## 改造目标与方案 目标是让 iOS 和鸿蒙按组件构建、发布 Native 产物,并共用公共基座。Gradle 管理依赖,插件与 CI/CD 负责打包、发布三端产物,再将新版本加入集成。 针对每种目标架构和构建配置,我们分别生成 stdlib、runtime 的公共基座,在本方案中称为 stdlib carrier 和 runtime carrier。业务组件通过函数、类型等符号引用基座,链接时再找到对应实现,不必重复携带 stdlib/runtime 的主体代码。Android 沿用 AAR 交付。 鸿蒙拆分编译以开源方案为基础。我们完成后续优化,并自研 iOS 分模块编译、Gradle 插件、组件模板和三端 CI/CD。  *图 2 总体方案:鸿蒙采用开源方案并继续优化,iOS 拆分交付由我们自主实现* ## KN编译器基础:华为鸿蒙开源方案 我们借鉴的鸿蒙开源方案为[华为跨平台框架社区KMP公版](https://gitcode.com/CPF-KMP-CMP/kotlin) 。开源方案为每个组件生成独立的 `.so`,供后续打包成 HAR。编译器以 klib(Kotlin/Native 的中间库)为单位,确定代码由哪份 `.so` 提供。每次编译都会读取所需依赖,但只生成当前组件负责的实现,对其他组件保留符号引用。 以组件 A 依赖组件 B 为例: * 编译 A 的 `.so` 时,生成 A 的实现,对 B 和基座保留外部符号引用; * 编译 B 的 `.so` 时,生成 B 的实现; * stdlib 和 Native runtime 分别生成基座 `.so`,业务 `.so` 不重复携带其主体实现。stdlib 基座保留公共 API,供业务组件调用。 该方案用 `moduleIncludes` 和 `outputModule` 选择本次生成的代码,用 `emitStdlib`、`emitRuntime` 控制是否生成对应的基础库实现。同时,调整 LLVM linkage(链接属性),并通过 `llvm.used` 标记需要保留的入口,避免它们被当作无用代码删除。 我们采用的开源版本要求在发布前重新编译全部集成组件。即使只修改了 A,其他组件也要重新编译、打包为 HAR,不能直接沿用旧包。 ## 鸿蒙端:KN编译器我们的后续优化 鸿蒙后续优化主要解决跨 `.so` 的桥接和类型编号不一致问题。 ### 1. 修复跨 `.so` 的桥接编号错位 Kotlin 代码调用 C / N-API 时,编译器会生成一小段负责转接调用的代码,这就是**桥接函数**。同一个文件中的桥接函数按处理顺序编号,名字可以简化为 `knbridge0`、`knbridge1` 等。这里的 `0`、`1` 只是顺序编号,并不代表固定功能。 组件 A 依赖 B。编译 A 时,编译器也可能读取并处理 B 中的 C / N-API 调用代码。同一处调用在编译 A、B 时,生成的桥接名必须一致。 问题是:编译 B 时会读取完整代码,而编译 A 时可能只读取 B 中实际用到的部分,编号顺序就可能变了。以 B 的同一个文件中“创建对象”和“读取回调参数”两处调用为例: ```text 编译 B:先处理“创建对象”,再处理“读取回调参数” knbridge0 → 创建对象 knbridge1 → 读取回调参数 编译 A:只处理用到的“读取回调参数” knbridge0 → 读取回调参数 ``` 这样,A 以为 `knbridge0` 是“读取回调参数”的入口,B 提供的同名入口却负责“创建对象”。运行时,系统按函数名查找实现,并不知道两边对这个名字的理解不同,A 的调用就可能落到错误的函数上。  *图 3 同一个桥接名对应了不同功能,统一读取和处理顺序后恢复一致* 我们的修复方式是:**各组件编译时,都把相关业务 klib 中的代码读全,再按相同顺序处理。** 这样,无论编译 A 还是 B,“创建对象”都对应 `knbridge0`,“读取回调参数”都对应 `knbridge1`,调用方和提供方就能对上。 读全代码是为了统一编号,不是把所有依赖都打进当前组件;各组件仍只生成自己的业务代码。这种方式可能增加编译耗时和内存占用;后续可根据函数名、参数类型等信息生成稳定的桥接名,不再依赖处理顺序。 ### 2. 修复跨 `.so` 的类型编号问题 Global Hierarchy Analysis(GHA,全局类层次分析)根据类和接口的继承关系分配编号(`classId` / `interfaceId`),并将类型判断和接口调用所需的信息写入编译结果。运行时利用这些信息,加快 `is`、`as` 和接口方法的查找。 单体编译共享一套类型集合和编号。拆分后,A、B 的 `.so` 各自编译,同一类型可能得到不同 ID。A 用自己的编号判断 B 创建的对象时,`is` / `as` 就可能出错,即使链接过程正常。 我们在鸿蒙拆分构建中关闭 GHA,改用不依赖全局编号的类型检查和接口调用方式。经业务测试确认,这一调整对运行性能没有较大影响。 ## iOS:KN编译器自研分模块编译 iOS 分模块编译和多个 Framework 之间的链接处理由我们自主实现。业务组件和公共基座分别生成静态 Framework,再封装为 XCFramework。App 链接时,由系统链接器 `ld64` 为各 Framework 的符号引用找到对应实现。 ### 1. 符号、桥接与共享状态 * **符号导出**:组件生成自己的实现,对依赖组件保留符号引用。我们也明确了哪些 C / Objective-C 互操作代码(cinterop)随当前组件生成。其他 Framework 需要调用的入口会被标记保留,避免被优化或链接裁剪(`dead_strip`)误删。 * **重复定义**:共享 cinterop klib 生成的部分枚举、Objective-C 基础类可能出现在多个 Framework 中。对于这些语义一致的重复定义,我们使用 LLVM `weak_odr`,允许链接器合并,避免重复符号冲突。这种处理只针对确认一致的定义,并非所有同名符号都能合并。 * **Objective-C 与 runtime 状态**:私有类名加上模块后缀,避免重名。每个业务 Framework 注册自己的类型适配表,避免被其他组件覆盖。共享配置由 runtime 基座提供,Objective-C runtime 注入逻辑增加检查,防止重复执行。 普通跨组件 Kotlin 调用仍使用原生符号,不引入额外的业务桥接层。 iOS 在判断其他 Framework 创建的对象类型时,也会遇到前述编号问题。关闭 GHA 的处理最初用于 iOS,随后应用到鸿蒙拆分构建。 ### 2. stdlib 按需导出 我们根据本次集成的 iOS 组件裁剪 stdlib。基座内部未使用的函数,可能正被业务 Framework 调用,裁剪时必须考虑这些调用。 以开启优化的 iOS Release 构建为例,设置 `stdlibPruneMode=input` 后,处理过程如下: **第一步:传入参与集成的业务 klib。** 构建 stdlib 基座时,启用 `emitStdlib=true` 和 `stdlibPruneMode=input`,传入当前 iOS 目标下的业务 klib 及其平台依赖。编译器读取业务函数体和类型信息,找出业务需要的 stdlib 实现。这些业务代码只参与分析,仍由各自组件生成。 **第二步:汇总组件用到的符号,补齐间接依赖。** 编译器先汇总各组件直接使用的 stdlib 函数、构造函数、字段和类型,再检查 vtable(虚方法表)、itable(接口方法表)、自动生成的 Objective-C 调用桥,以及全局变量的初始化代码。这些地方也会引用 stdlib。沿调用和类型关系继续查找,就能得到“必须保留的符号集合”。 例如,业务枚举对 `kotlin.Enum` 继承方法的引用可能只出现在 vtable 中,某些类型转换只由生成的 Objective-C 桥调用。漏掉这些符号会导致链接或初始化失败。 在内联、去虚化等关键优化前,编译器先保存一份符号需求,供后续分析合并。基座编译时可能把某个调用展开或改写掉,但其他独立编译的 Framework 仍需要按原符号找到它。 **第三步:根据符号需求删除无用代码(DCE)。** Dead Code Elimination(DCE,死代码消除)从业务所需符号和必要的运行时入口出发,保留它们直接或间接依赖的函数、类型和初始化代码,再删除其余实现。这样就不必默认保留 stdlib 的全部公共函数。 **第四步:保护 LLVM 链接入口。** 在 Intermediate Representation(IR,中间表示)层完成裁剪后,还要防止 LLVM 再次误删代码。基座中实际生成并保留下来的函数,继续使用外部链接属性(`ExternalLinkage`),并加入 `llvm.used` 保留清单。它们虽然可能没有本库调用方,仍需供其他 Framework 链接使用。类型信息和部分静态全局符号也做相应保护。 **第五步:链接时由基座提供实现。** stdlib 主体由基座生成,业务 Framework 保留对它的外部引用。宿主 App 链接各静态 Framework 时,由 stdlib 基座提供定义。  *图 4 input 模式:分析业务需求 → 裁剪无用代码 → 保护链接符号 → 链接各 Framework* 定制分支也支持通过文件传递符号清单:业务编译时,用 `stdlibDepsOutput` 按行输出所需符号;基座通过 `stdlibDepsInput` 读取、合并这些清单,补齐依赖后裁剪。`input` 模式直接分析业务 klib,无需先编译业务 Framework 来收集清单。 这里导出的符号用于 Native 链接,不会把整个 stdlib 变成 Swift / Objective-C 可见的 API。参与集成的组件或依赖变化后,需要重新分析符号需求,必要时重新生成基座。 ## 自研 Gradle 插件:按组件生成 HAR 和 XCFramework 插件提供鸿蒙和 iOS 单组件打包任务,通过 `moduleName` 选择要构建的组件。业务在 Gradle source set(源码集)中声明依赖,通过 version catalog(版本目录)管理依赖坐标和版本。脚本指定本次构建的组件及版本;插件设置编译参数、整理对外接口,再打包成 HAR 或 XCFramework。以聚合模块 `KMPBom` 中的组件 A 为例: ```shell # iOS ./gradlew :KMPBom:podPublishReleaseModuleXCFramework \ -PmoduleName=com.example:moduleA -Pversion=1.1.0 # 鸿蒙 ./gradlew :KMPBom:assembleModuleHarReleaseOhosArm64 \ -PmoduleName=com.example:moduleA -Pversion=1.1.0 ``` 将 `moduleName` 换成 `std` 或 `runtime`,即可构建对应基座。插件根据模块配置设置 `moduleIncludes`、`outputModule`、`emitStdlib`、`emitRuntime` 等参数。 ### 1. 确定每个组件生成哪些代码 Gradle 根据组件坐标、版本和目标平台,解析对应的 variant(平台变体),取得本次构建所需的 klib。执行 Native link(链接)任务前,插件读取 klib manifest(库描述文件):通过 `unique_name` 识别库,通过 `depends` 查找依赖。 插件据此生成 `moduleIncludes`,告诉编译器“每个产物包含哪些 klib 的代码”;`outputModule` 指定“本次生成哪个产物”。以 A 依赖 B 的 iOS 业务编译为例: ```text moduleIncludes={moduleA:[com.example:moduleA];moduleB:[com.example:moduleB]} outputModule=moduleA emitStdlib=false emitRuntime=false ``` 编译器会读取 A、B 的 klib,本次只生成 `moduleA` 分组的实现。B 的实现由 B 的库提供,stdlib/runtime 由各自基座提供。 两端查找依赖、生成分组的方式有所不同: * **鸿蒙**:从目标 klib 的 `depends` 开始,逐层查找组件及其依赖,再将业务 klib 分组。stdlib、平台库和 cinterop 单独处理,不放入普通业务分组。 * **iOS**:从 Gradle 已找到的 `iosArm64` klib 中读取目标组件的 manifest,按 `depends` 建立分组,并用依赖的 `short_name` 确定组名。插件不额外递归查找下一层依赖,同时保留平台库和 cinterop 的分组信息。 ### 2. 鸿蒙:从 klib 准备独立 HAR 工程 组件发布 klib 时,插件会写入 KSP 生成的内容和组件信息,包括名称、版本、OHPM 依赖及是否需要 N-API 入口。聚合模块的插件在出包时读取这些信息。 出包分为四步: 1. **选择本次生成的代码。** 根据 `moduleName` 确定目标组件,默认不在业务库中生成 stdlib/runtime 主体。`std`、`runtime` 等特殊产物通过 `kmp-har-config.json5` 配置;也可以在该文件中调整模块名、编译参数和 HAR 信息。 2. **准备需要链接的库,生成 `.so`。** 链接前读取 manifest 中的 `linkerOpts`,选出应由当前组件链接的原生库参数,并保留平台必需的参数。随后安装对应的 OHPM 依赖,把 `.so` 所在目录加入搜索路径,再调用定制编译器。`moduleIncludes` 决定生成哪些 Kotlin 代码;链接哪些原生库,需要单独处理。 3. **准备 HAR 工程。** 从目标 klib 读取组件信息,优先提取其中的 ArkTS 模板,只合并当前组件的类型声明和导出内容。复制生成的 `.so` 及所需头文件,再写入包名、版本、组件依赖和基座依赖。如果需要 N-API 入口,还会准备加载器(loader)的初始化代码和 CMake 配置,交给下一步 hvigor 编译;不需要这一入口的基础库可以只携带自身 `.so`。 4. **生成 HAR。** 执行 `ohpm install` 和 `hvigorw assembleHar`,收集产物并按模块命名。 ### 3. iOS:按组件导出接口,生成 XCFramework iOS 插件复用 CocoaPods 构建任务,在模板已有的静态 Framework 配置上,根据本次构建对象设置参数: * **业务组件**:确定该组件需要生成哪些代码,不重复生成 stdlib/runtime 主体; * **stdlib**:开启 `emitStdlib` 和 `stdlibPruneMode=input`,直接分析业务 klib 的符号需求; * **runtime**:只开启 runtime 主体代码的生成。 插件先清空聚合工程原先导出全部组件的 `export` 设置,再让业务 Framework 只导出当前组件;stdlib/runtime 基座不导出业务组件。进入链接任务前,插件会再次应用这些设置,防止其他 Gradle 配置把导出范围改回全部组件。 插件还会按目标组件设置 CocoaPods 名称、Framework 名称和 podspec 版本。各架构的静态 Framework 生成后,由 CocoaPods 任务将它们组合为 XCFramework,连同 podspec 一起交给流水线上传发布。 插件会将实际使用的编译参数和导出配置写入 `build/parameter/ios-cocoapods.gradle.kts`,并为每个模块留存一份,便于排查符号缺失、接口导出和命名问题。 ### 4. KMP 组件间依赖如何传到 HAR、XCFramework 之间 以 A 依赖 B 为例:拆分后,B 仍独立出包,依赖关系对应 `A.har → B.har` 和 `A.xcframework → B.xcframework`。编译器保留 A 对 B 的符号引用,包管理工具则根据依赖声明,为 App 同时安装 A、B 两份产物。 **鸿蒙:让 A.har 声明对 B.har 的依赖。** 这部分已由插件实现: 1. 从 A 的 klib manifest 读取依赖,找到 B,再逐层查找 B 依赖的组件。 2. 按组件坐标找到对应 klib,读取组件信息中的 `ohpmName`,得到 HAR 包名;未配置时按默认规则生成包名。 3. 将这些包名写入 A 的 `oh-package.json5` 的 `dependencies`。业务组件不携带 stdlib/runtime 主体时,再补上两份基座 HAR 的依赖。组件中声明的原生 OHPM 依赖,也通过上述组件信息传递。 App 工程安装 A 时,OHPM 根据声明取得依赖包。各 HAR 中的 `.so` 随平台打包进入 App,运行时由系统动态链接器找到跨库调用的实现。B 的主体实现保留在自己的 HAR 中。 自动写入的组件及基座 HAR 依赖版本目前是 `*`,不会直接沿用 KMP 构建时选定的版本,集成时仍需指定能配套使用的版本。 **iOS:通过 Pod 依赖关联 A.xcframework 和 B.xcframework。** 两份 XCFramework 独立存放,由配套的 podspec 声明包依赖。iOS 插件完成以下步骤: 1. 读取当前组件的 KMP 依赖,结合本次 Gradle 解析结果,确定依赖组件对应的 Pod 名称和版本。 2. 在 A 的 podspec 中生成对 B 的 `spec.dependency`,并声明所需的 stdlib/runtime 基座。若 B 还依赖 C,则在生成 B 的 podspec 时继续声明 C。 3. App 接入 A 对应的 Pod 后,CocoaPods 根据声明取得 B、C 及基座的 XCFramework,一并加入工程。App 链接时,再为各 Framework 的符号引用找到对应实现。 `export` 只控制 Objective-C 接口导出,不能代替 podspec 的包依赖声明。 ### 5. 单独更新组件,不需要重新生成整套产物 以 iOS 为例:只要现有依赖和基座仍能配套使用,就可以通过 `moduleName` 指定待更新组件,只生成它的 XCFramework。 假设 App 已经接入 A、B、C 和两份公共基座,现在修改 A: * **只改 A 的业务逻辑,现有依赖仍能配套使用。** 只生成并发布 A 的新包,App 工程更新 A 的版本;B、C 和基座继续使用原来的产物。 * **A 需要的函数,旧基座没有提供。** 例如 A 新调用的 stdlib 函数已被旧基座裁掉。此时还要汇总新版 A 与现有 B、C 的符号需求,重建 stdlib 基座,保留所有组件需要的函数。 将新包与继续使用的旧包放在一起验证,通过后再更新 App 工程的依赖。 首次接入且没有可用产物时,需要分别为各业务组件和两份基座出包。目前仍需人工确定要重新构建哪些组件,插件不会根据代码改动自动判断。  *图 5 iOS 更新场景:只更新 A,或同时更新 A 与 stdlib 基座;新旧包一起验证后再集成* ## 自研 CI/CD:组件构建一次,发布三端产物 发起一次组件构建,流水线分别完成 Android、iOS 和鸿蒙的编译打包,发布 AAR、XCFramework 和 HAR;随后添加集成,将组件版本写入对应平台集成单。  *图 6 两层模板完成三端构建;产物发布后添加集成,再更新平台集成单,供后续构建读取* ### 1. 两层模板:外层聚合模块,内层业务组件 ```text component-template/ ├── settings.gradle.kts # 注册 iOS、鸿蒙两份版本配置 ├── 集成单-iOS.json ├── 集成单-鸿蒙.json ├── gradle/ │ ├── bom.ios.versions.toml # iOS 组件坐标与版本 │ └── bom.ohos.versions.toml # 鸿蒙组件坐标与版本 ├── Script/kmp_build.sh └── KMPBom/ # 外层:聚合模块 ├── build.gradle.kts # 引用两端版本配置,声明组件依赖 └── moduleA/ # 内层:业务组件 ├── build.gradle.kts └── src/ ``` 两个模块属于同一个 Gradle 构建: * **内层业务组件**:存放业务源码、声明组件自身依赖,编译并发布各 Native 目标的 klib,以及 Android AAR。 * **外层聚合模块**:根据 iOS、鸿蒙两份版本配置找到所需组件,再用插件为选定组件生成 XCFramework 或 HAR。它读取已发布的 klib,不通过 `project(...)` 直接引入内层源码。 `settings.gradle.kts` 将两份 toml 注册为 Gradle version catalog:iOS 使用 `iosBoms`,鸿蒙使用 `ohosBoms`。外层 `KMPBom/build.gradle.kts` 通过它们添加依赖。例如,`api(iosBoms.componentA)` 就是把 iOS toml 中 `componentA` 对应的组件和版本加入依赖。以下用 `componentA` 作为示意别名: ```kotlin kotlin { sourceSets { iosMain.dependencies { api(iosBoms.componentA) // 使用 bom.ios.versions.toml 中的版本 } ohosMain.dependencies { api(ohosBoms.componentA) // 使用 bom.ohos.versions.toml 中的版本 } } } ``` 外层准备好编译所需的依赖,再由 `moduleName` 决定本次为哪个组件出包。 新组件接入时,修改内层模块名并声明业务依赖,在两份 toml 中登记组件坐标和版本,再将组件加入外层依赖。 ### 2. 构建前:拉取集成单,修改工程依赖版本 平台集成单列出该平台已选用的组件及版本。iOS、鸿蒙构建前,先把这份清单拉到模板工程,再更新工程中的依赖版本: 1. **拉取平台集成单。** 将 iOS、鸿蒙集成单分别保存到工程根目录的 `集成单-iOS.json`、`集成单-鸿蒙.json`,供构建脚本读取。 2. **修改工程依赖版本。** 脚本将集成单中的组件版本写入 `gradle/bom.ios.versions.toml` 或 `gradle/bom.ohos.versions.toml`。 如果 iOS 集成单指定 A 为 `1.0.0`,脚本就把 `bom.ios.versions.toml` 中 A 的版本改为 `1.0.0`。外层仍通过 `api(iosBoms.componentA)` 引用 A,但 Gradle 此时取得的是 A `1.0.0` 的 klib。鸿蒙同理。 脚本只修改已有组件的版本,不会自动把新组件加入聚合模块。 ### 3. 两阶段构建:内层发布 klib,外层生成平台产物 1. **内层组件先发布 klib。** 内层将当前源码编译为所选 Native 目标的 klib。模板默认发布到工程内的 Maven 仓库,供外层读取。 2. **外层聚合模块再出包。** 脚本把当前组件的依赖版本改为刚发布的 klib 版本,其他组件仍使用集成单中的版本。外层取得这些 klib 后,执行 XCFramework / HAR 任务,为 `moduleName` 指定的组件出包。 如果内层本次发布 A 的 `1.1.0` klib,外层就将 A 的依赖版本从 `1.0.0` 改为 `1.1.0`,再生成平台产物。此时修改的是当前工程的依赖;平台集成单要等三端产物发布、添加集成后再更新。 同一平台、架构下,配套使用的 KMP Native 库需要统一编译器版本、构建模式(Debug/Release)、垃圾回收(GC)与 runtime 配置、Objective-C 导出配置和拆分参数,再放在一起验证。 采用前述鸿蒙开源版本时,发布前仍需重新编译全部集成组件,再分别打包为 HAR。 Android 按原有流程生成并发布 AAR,由 Gradle 管理组件依赖。三端共用发布入口,各自按平台方式出包。 ### 4. 发布三端产物,添加集成,再写入平台集成单 指定组件和版本后,流水线完成构建、打包和产物核对,再依次执行: 1. **发布三端产物。** 上传 Android AAR、iOS XCFramework 和鸿蒙 HAR,确保对应版本可获取。 2. **添加集成。** 选择本次要纳入集成的组件及其已发布版本。 3. **写入对应平台集成单。** 新组件新增条目,已有组件更新版本,供后续构建读取。 A 的 `1.1.0` 三端产物发布并添加集成后,写入对应平台集成单,随后发送通知。其他组件下次构建时重新拉取集成单,将 A 的 `1.1.0` 带入自身外层聚合模块的依赖。 发布脚本会列出产物及体积。如果某个组件的包突然变大,应检查它是否正确引用公共基座,以及 `moduleIncludes` 中该组件的分组是否误包含了其他组件的 klib。 交付前还需验证: * **符号链接**:检查未定义符号、重复定义、导出符号和依赖库,确认跨组件调用能找到对应实现; * **组件联调**:将组件放在一起,验证跨组件调用、OHOS 加载顺序、`is` / `as`、接口方法调用、全局初始化及 Objective-C / N-API 桥接; * **配置兼容**:核对编译器版本、基座的二进制接口(ABI)、构建模式和拆分参数,确保各个包能配套使用。 已发现的桥接编号和类型检查问题应加入跨组件回归测试,防止后续改动再次引入同类问题。 ## 落地收益 * **鸿蒙启动提速**:缩短 App 启动耗时。 * **KMP 编译提速**:减少单次编译耗时。 * **矩阵 App 按需集成**:矩阵 App根据各个 App 的业务需求,按需集成 KMP 组件的各端产物:Android AAR、iOS XCFramework 和鸿蒙 HAR,避免带入不需要的KMP组件。 ## 总结与展望 我们在鸿蒙开源方案上完成后续优化,并自研 iOS 分模块编译、链接适配和 stdlib 按需导出。Gradle 插件、两层组件模板与 CI/CD 将拆分能力接入构建和发布流程,使业务组件与公共基座分别交付。 接下来继续优化包大小,减少 stdlib、互操作和桥接代码中的冗余,并验证裁剪后的跨组件链接与运行结果。
上一篇:拆解海博 AI-Native 落地保障:海博团队 AI 知识库能力建设
下一篇:JDD Oxygen智能零售论坛 | 《Agentic广告营销新范式》
he****
文章数
1
阅读量
58
作者其他文章
01
京东攻克业界难题: KMP 组件交付从大仓模式到分仓模式
背景与现状我们用 KMP 复用 Android、iOS 和鸿蒙的业务逻辑。目前,各组件分别维护源码仓库、依赖和版本,再打包成各平台可用的库,供 App 工程集成。改造前的交付方式(业界通用方案):Android:通过 AAR / Gradle Module 交付,由 Gradle 管理依赖。iOS:将全部 KMP 业务模块打进一个 Umbrella Framework(聚合 Framework)。
he****
文章数
1
阅读量
58
作者其他文章
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号