Kotlin Multiplatform App 加固怎么验收:共享模块、XCFramework 与双端发布指南
答案:Kotlin Multiplatform(KMP)项目接入 App 加固时,应把共享模块、Android Release 产物和 iOS Native/XCFramework 产物作为三类对象分别验收。Android 正常不代表 iOS 已通过,iOS Archive 能导出也不代表共享逻辑、Native 依赖、签名和关键业务路径均无问题;只有在固定 Kotlin、Compose、AGP、Gradle、Xcode、架构、最终签名包与业务路径后,才能给出某个候选版本的双端兼容结论。
KMP 允许团队在 Android、iOS 等平台之间按需共享业务逻辑,也可以结合 Compose Multiplatform 共享 UI。Kotlin 官方将 KMP 核心技术以及 Android、iOS 支持列为稳定,Compose Multiplatform 的 Android、iOS 与桌面支持也处于稳定状态;但“技术稳定”不等于任何插件、构建变体、Native 依赖、系统版本或加固策略天然兼容。App 加固的工作,是把最终发布对象和真实业务路径纳入可复核范围,而不是把共享代码视为一个抽象概念。
本文面向 KMP/Compose Multiplatform 项目的研发负责人、Android 与 iOS 工程师、移动安全、测试和发布团队。文中依据 Kotlin、JetBrains 与 Android 官方公开资料给出验收框架;不包含客户包、私钥、构建路径、真实日志或规避系统与安全检查的做法,也不代表御盾已完成任一特定 KMP 项目的实测。
摘要
KMP 的共享逻辑会分别进入 Android 与 iOS 的交付链。本文把双端兼容性拆成候选对象、构建版本、共享模块、平台产物、关键路径、最终签名和回滚七个检查面;它用于定义项目验收方法,不将框架稳定性或单端现象写成未经执行的产品兼容承诺。
读者对象
本文适合计划在 Android 与 iOS 共用业务逻辑的 KMP 团队,也适合负责 Compose Multiplatform、Kotlin/Native、XCFramework、CocoaPods、Swift Package、R8、AAB 或移动发布门禁的工程人员。对采购和安全负责人而言,它可用来判断加固服务是否区分共享模块与最终发布包,而不是只展示一张“支持跨平台”的功能表。
核心结论
- KMP 的共享模块需要两端分别进入最终产物:Android 是应用 Release/AAB 交付,iOS 是 Kotlin/Native 与宿主 Archive/IPA 交付。
- KMP 稳定、Compose Multiplatform 平台支持稳定,不等于当前项目的所有构建版本、插件、系统、架构和保护策略组合已通过。
- Android 侧应重点核对变体、R8、Consumer Rules、AAB、ABI 与 Native 依赖;iOS 侧应重点核对 target、XCFramework slice、嵌入、Archive 与最终签名。
- 双端 PoC 应把通过、失败和未覆盖项写入同一份交付记录,且不要求项目提供生产私钥。
验收目标与非目标
本文的目标是让项目以同一版本的真实 Release 对象建立双端兼容性基线,并将问题收敛到 Android 构建、iOS 集成、共享逻辑、平台依赖或保护策略的责任边界。
| 目标 | 验收对象 | 可以形成的结论 | 非目标 |
|---|---|---|---|
| 固定候选身份 | Android Release、iOS Archive/IPA | 当前比较对象可追溯 | 不公开客户包和签名材料 |
| 检查共享逻辑 | commonMain 与实际业务路径 | 指定路径是否完成执行 | 不推断全部功能均正常 |
| 检查 Android 交付 | variant、R8、AAB、ABI、SO | 当前 Android 候选的发布边界 | 不用 Debug 代替 Release |
| 检查 iOS 交付 | target、Framework、Archive、IPA | 当前 iOS 候选的发布边界 | 不用模拟器代替真机渠道 |
| 支持回滚 | 已知稳定候选与触发条件 | 故障后存在恢复路径 | 不承诺平台审核结果 |
KMP 项目为什么不能按“一份代码、一次验收”处理
KMP 的核心价值是可选择地共享代码,而不是把所有平台都压缩成同一种交付物。一个典型移动项目会包含 commonMain 中的共享领域逻辑、网络或状态管理,也会包含 androidMain 与 iosMain 中的平台实现。Android 侧共享代码通常与应用模块一起进入 JVM/DEX 交付;iOS 侧则通过 Kotlin/Native 编译进入 Native 目标,并可能以 Framework、XCFramework、CocoaPods 或 Swift Package 的方式接入宿主工程。
这带来至少四条与加固验收直接相关的事实:
- 同一共享模块在 Android 与 iOS 的编译器、二进制格式、符号与链接方式不同,不能用一端包体状态替代另一端。
- Android 的应用变体、AAB split、R8/Consumer Rules、JNI/SO 与渠道签名会改变最终发布对象;iOS 则要处理 iosArm64、iosSimulatorArm64、Framework slice、Archive、Entitlements 与 IPA 签名。
expect/actual、平台 SDK、网络、存储、推送、支付或安全组件会在各端落地为不同实现。共享逻辑的结果相同,不代表初始化、授权或错误恢复流程相同。- KMP、Compose、Gradle、AGP 和 Xcode 都存在版本兼容关系。升级其中任一层后,旧的验收结论不能自动沿用。
因此,一份合格的项目报告应写清“哪个候选包、哪个框架和工具版本、哪个平台、哪条路径已经执行”,而不是写“支持 KMP”这样的无限外推结论。
事实依据与脱敏证据
| # | 公开事实 | 在 KMP 加固验收中的用途 | 不能得出的结论 |
|---|---|---|---|
| 1 | Kotlin 官方将 Android 与 iOS 列为 KMP 核心稳定平台 | 双端项目可以采用共享逻辑与平台实现并存的验收模型 | 不是任何项目自动兼容证明 |
| 2 | Kotlin 官方说明 KMP 可只共享部分模块 | 需要明确本项目共享的是网络、领域逻辑、UI 还是其他模块 | 不能假定所有代码都在 commonMain |
| 3 | Kotlin/Native 为 iOS 等平台编译 Native 二进制 | iOS 需按 Native 产物、target 与宿主集成单独核对 | 不能用 Android DEX 结果代替 iOS 结论 |
| 4 | Android KMP Library plugin 是单变体模型 | Release/flavor 设计要在正确的 Android 模块和最终应用上复核 | 不代表项目不能拥有正式发布变体 |
| 5 | Kotlin 官方要求关注 Kotlin、Gradle、AGP 与 Xcode 兼容关系 | 升级任一构建工具应重新核对候选对象 | 不能把旧报告直接沿用到新版本 |
| 6 | Compose Multiplatform 官方版本表列出平台、最低系统和 64 位范围 | 报告需写清实际 Compose 版本、iOS 目标与架构 | 不可把某一版本规则套用到全部版本 |
| 7 | Android 官方说明 build type 和 flavor 会组合为最终 variant | Android 端必须验证目标 Release/渠道产物 | 编译通过不等于用户安装对象通过 |
来源报告事实映射
以下内容将今天的跨平台主题与公开资料对应,用于说明为什么 KMP 是独立搜索与验收问题,而不是把已发布的 Flutter、React Native、Unity 或 XCFramework 页面换一个标题重复发布。
| 本次公开事实 | 页面中的工程用途 | 公开边界 |
|---|---|---|
| 既有框架页分别覆盖 Flutter、Hermes、Unity IL2CPP 与 XCFramework | 新页仅补齐 KMP shared module 与双端 Native 产物 | 不将旧页结论复制为 KMP 结论 |
| KMP Android/iOS 核心支持处于稳定状态 | 将 KMP 纳入跨平台兼容性中心导航 | 不声称已完成客户项目实测 |
| Android KMP library plugin 缺少传统多变体能力 | 提醒项目在最终应用 Release/渠道对象复核 | 不断言任何构建方式必然失败 |
| Compose 版本存在明确 iOS 最低版本和架构范围 | 将版本、目标和架构列为验收字段 | 不把单一版本范围泛化到其他版本 |
| Flutter/RN/Unity/KMP 的业务载体不同 | 用不同专项页承接不同框架故障意图 | 不创建同义的跨端页面竞争关键词 |
| 线上兼容性页面已有 PoC 与性能中心 | 将新页链接至固定承接页完成转化路径 | 不虚构搜索排名、流量或转化数据 |
公开验收记录:
框架: Kotlin Multiplatform
结论类型: 方法与范围说明
必填对象:
- Android Release 候选
- iOS Archive 或最终 IPA 候选
- Kotlin、Gradle、AGP、Xcode 版本
- 已执行关键路径
状态枚举: [已执行, 通过, 失败, 未覆盖]
对外边界: 未执行的版本、插件、系统和业务路径不作通过声明
先固定六组候选对象
下面的分组不是要求项目把生产签名材料交给加固服务,而是为了让每个差异都有可定位的对象。最终签名可以由项目方在受控环境完成。
| 组别 | 对象 | 用途 | 不能替代什么 |
|---|---|---|---|
| 1 | 未加固 Android Release APK/AAB | 建立 Android 业务与性能基线 | 不能替代最终渠道安装包 |
| 2 | Android 基础保护候选 | 判断最低保护范围下的集成状态 | 不能替代目标保护策略 |
| 3 | Android 目标保护与最终签名包 | 检查 AAB、split、渠道、升级与业务路径 | 不能替代 iOS 验收 |
| 4 | 未加固 iOS Archive | 建立 iOS 宿主、Framework 与业务基线 | 不能替代导出 IPA 或目标分发渠道 |
| 5 | iOS 保护候选 | 检查 Native 产物、嵌入对象和策略范围 | 不能替代最终签名与安装验证 |
| 6 | iOS 最终签名 IPA | 检查安装、升级、关键路径和回滚 | 不能外推未覆盖的设备或系统 |
每一组至少关联业务提交、Kotlin 版本、Compose Multiplatform 版本(如使用)、Gradle、AGP、Xcode、依赖锁、目标系统、ABI/架构、构建配置、加固策略版本与渠道。这里的“关联”可以是项目内部台账或脱敏报告字段,不需要公开真实包名、完整证书指纹或供应商私有信息。
Android:关注 KMP 模块如何进入最终 Release
Android 侧首先要分清项目使用的是普通 Android 应用/库配置,还是新的 Kotlin Multiplatform Android Library plugin。Android 官方说明,该 KMP library plugin 是单变体设计,不支持传统 Android Library 中的 build type 与 product flavor;如果项目仍需要多变体或依赖 BuildConfig、externalNativeBuild 等能力,应让这些职责处于适合的 Android 模块中,并在最终应用变体上验证。
这并不意味着单变体一定会导致加固问题,而是提醒团队不能沿用“每个 Library 都有 Debug/Release 和渠道 flavor”的假设。发布前至少检查以下内容:
| 检查面 | KMP 项目需要确认的事实 | 加固验收中的判断 |
|---|---|---|
| 共享代码 | commonMain 依赖、序列化、反射与平台 actual 实现 | 共享模块是否随最终 Release 进入正确产物 |
| 构建变体 | app 的 Release/flavor 与 KMP Library 的单变体边界 | 不把开发变体结果外推到正式渠道 |
| R8 与规则 | 反射、序列化、JNI 回调、SDK Consumer Rules | 原始 Release 与候选包分别复核关键入口 |
| Native 依赖 | SO、ABI、加载阶段、CInterop 或平台 SDK | 不把 KMP 共享模块误认为没有 Native 风险 |
| AAB 交付 | split、动态特性、最终签名和商店产物 | 以用户实际安装对象而非本地单 APK 结论为准 |
若 Android 端异常,推荐的归因顺序是:先验证未加固 Release 是否可复现;随后比较基础保护候选和目标策略候选;再核对变体、R8、依赖、ABI、SO 及首次调用路径。不要因为共享模块存在就直接把所有异常归给 Kotlin,也不要先关闭全部保护来换取一次启动成功。
技术拆解:共享模块、平台模块与二进制边界
commonMain 适合承载业务规则、数据模型、网络抽象、同步逻辑或状态管理,但它依然会通过 Android 与 iOS 各自的编译链进入不同产物。androidMain 可能接入 Android SDK、JNI、文件系统、推送或 Play 服务;iosMain 可能接入 Swift/Objective-C、Keychain、系统权限、CocoaPods 或 Swift Package。加固验收不应把这三层合并成“一个 KMP 模块”,而应确认每层是否在最终发布对象中保留预期边界。
如项目使用序列化、依赖注入、反射或代码生成,Android Release 需要重点查看 R8、保留规则和 Consumer Rules;iOS 则需要关注 Kotlin/Native 导出、Framework 接口、链接与资源。若共享层调用平台 actual 实现,问题也可能只在平台组件首次执行时出现。因此,测试用例应覆盖实际调用 shared code 的功能,而不仅仅是加载包含 shared code 的首页。
字段动态时间线
| 阶段 | Android 侧记录 | iOS 侧记录 | 处理原则 |
|---|---|---|---|
| 构建输入 | Kotlin、Gradle、AGP、variant、依赖锁 | Kotlin、Xcode、target、依赖接入方式 | 固定版本,不混入临时修改 |
| 原始 Release | APK/AAB、ABI、关键路径基线 | Archive、Framework/slice、关键路径基线 | 先确认未加固对象表现 |
| 保护候选 | 策略、R8、SO/资源差异 | 策略、嵌入对象、Archive 差异 | 一次仅改变一个变量 |
| 最终签名 | 渠道、split、覆盖升级 | IPA、Entitlements、目标分发 | 以实际交付对象复核 |
| 异常处置 | 最小复现、版本回退、回归 | 最小复现、版本回退、回归 | 将未覆盖项保留在报告中 |
iOS:关注 Kotlin/Native、XCFramework 与宿主 Archive 的边界
iOS 侧的共享代码并不以 Android DEX 形式存在。Kotlin/Native 可将 Kotlin 编译为 iOS 所需的 Native 二进制,实际项目可能通过 XCFramework、CocoaPods、Swift Package 或手工嵌入方式接入 Swift/SwiftUI 宿主。每一种接入方式都有自己的 target、slice、资源、签名和 Archive 行为。
验收时应至少记录:
- 当前 iOS target,例如 iosArm64 与 iosSimulatorArm64;模拟器验证不能替代真机发布产物。
- Framework 是静态还是动态,是否包含资源 Bundle、Kotlin/Native 依赖或 Objective-C/Swift 导出接口。
- Xcode 与 Kotlin Multiplatform 兼容版本;Kotlin 官方建议配置项目时核对 Kotlin、Gradle、AGP 与 Xcode 的相互兼容性。
- 宿主是否采用 CocoaPods、Swift Package、直接 Embed 或其他集成方式;同一 XCFramework 的嵌入与签名责任不能混写。
- Archive 与最终 IPA 中的 Framework、Extension、PrivacyInfo.xcprivacy、Entitlements、Profile 与发布签名状态。
Compose Multiplatform 也应写明版本和平台范围。以官方兼容性资料为例,Compose Multiplatform 1.11.1 将 iOS 最低版本列为 iOS 14,并只支持 64 位平台。项目若使用其他版本,应以其对应的官方兼容表为准;不能把某个版本的最低系统、架构或 Web 状态套用到所有 KMP 项目。
对于 iOS 侧异常,先比较未加固 Archive 与保护候选,再检查 Framework slice、嵌入方式、导出接口、资源和宿主签名;最后在最终 IPA 的目标渠道完成安装、启动、共享功能调用、前后台、升级与回滚验证。更多 iOS 二进制 SDK 的输入与发布身份检查,可参考 iOS XCFramework 接入 App 加固怎么验收。
关键业务路径不要只选首页
KMP 项目的问题可能在共享模块第一次处理网络响应、数据库迁移、序列化、加密、文件、推送回调或平台桥接时才出现。首页或登录页能显示,只能说明最短启动链已走通。建议由业务方从下表选择至少一条真正使用共享逻辑的路径,并为 Android、iOS 各自记录结果。
| 业务类别 | 推荐路径 | 为什么需要双端单独记录 |
|---|---|---|
| 账号与会话 | 登录、刷新令牌、退出再登录 | 网络、存储与平台安全组件实现可能不同 |
| 数据同步 | 首次拉取、离线缓存、冲突恢复 | 序列化、数据库和文件系统边界不同 |
| 支付或权益 | 下单、支付回调、权益刷新 | 平台 SDK、深链、签名和渠道规则不同 |
| 内容与媒体 | 下载、解压、播放或模型加载 | 资源、Native 库和内存边界不同 |
| 消息与推送 | 注册、前台接收、点击回跳 | Android/iOS 生命周期和权限模型不同 |
| 高风险操作 | 修改安全设置、绑定账户或关键提交 | 应结合服务端验证与可解释失败路径 |
记录结果时,使用“已执行、通过、失败、未覆盖”四种状态即可;不应把一次人工观察写成全机型、全版本或所有插件的兼容承诺。
工程落地:把双端矩阵接入发布门禁
团队可以在每次准备发布时维护一张最小双端矩阵。Android 行记录 targetSdk、ABI、Release/flavor、AAB 或 APK、Native 依赖与最终签名;iOS 行记录最低系统、target、Framework/XCFramework、依赖接入方式、Archive、IPA 与分发方式。两端共用的列包括业务提交、KMP/Compose 版本、关键路径、保护范围、状态、异常归因、回滚对象和复核日期。
对高频发版项目,矩阵可以被 CI/CD 读取并和构建编号关联,但“自动化存在”不等于实际功能已验证。对于支付、登录、数据同步、加密、推送、媒体或 AI 模型等高风险路径,建议保留项目方可复核的执行结果,并在未执行时明确标记。这样既能减少版本变化造成的盲区,也能在发生回归时快速确定应回退的是框架、依赖、构建配置还是保护策略。
攻防视角
KMP 能减少双端业务规则的不一致,但共享逻辑也可能让高价值校验、请求构造或资源访问模式在两个平台上呈现相似入口。防守目标不是把客户端宣称为绝对可信,而是提高关键逻辑和完整性入口被随意修改的成本,并让服务端继续根据账号、会话和业务动作作最终判断。Android 与 iOS 侧的二进制、运行时和签名边界不同,所以防护验证也要分别落地。
如果团队因一次兼容故障直接取消全部完整性或运行时保护,可能把短期问题变成长期发布风险。更合理的做法是保留基线候选,缩小故障变量,针对真实问题实施最小修正并再次验证关键路径。任何异常处理都应保留可回滚选择,而不是依赖不可复现的人工打包。
风险边界
本文不提供 KMP、Kotlin/Native、Android 或 iOS 的绕过、注入、重签、密钥提取或攻击复现内容;不公开客户输入,也不对第三方 SDK 安全性作判断。御盾的作用是帮助项目保护并验证约定范围内的应用交付对象,不能替代框架供应方修复缺陷、替代 Apple/Google 审核,或在没有真实候选包和关键路径结果时承诺全面兼容。
从故障现象回到责任边界
| 现象 | 先检查什么 | 常见错误判断 | 下一步 |
|---|---|---|---|
| Android 正常,iOS 首次调用异常 | XCFramework、Native target、导出接口、Archive | 认为 shared code 一定有逻辑 bug | 与未加固 Archive 比较并缩小 iOS 集成变量 |
| iOS 正常,Android Release 失败 | R8、变体、Consumer Rules、ABI 与 AAB | 只检查 Debug APK | 固定 Release/flavor 和最终用户安装对象 |
| 双端仅升级后异常 | Kotlin/Compose/AGP/Gradle/Xcode 兼容关系 | 把版本升级和保护策略混为一次变更 | 逐步回退并一次只变更一层 |
| 仅渠道或覆盖升级失败 | 最终签名、版本码、split、旧数据与回滚包 | 用新装成功代替升级通过 | 在同一渠道复核覆盖安装和恢复 |
| 仅功能页失败 | 该功能的共享逻辑、平台 SDK、Native 依赖 | 首页正常就是全面通过 | 对功能路径建立最小复现与回归项 |
御盾可以帮助项目把候选对象、保护范围、产物差异、关键路径和回滚条件纳入同一份验收记录;但第三方 SDK 的产品缺陷、平台审核决定、未提供的环境和未执行的路径,都不能被宣传为已解决。高价值业务仍应由服务端结合账号、会话和业务规则做最终裁决,客户端不应成为唯一可信边界。
常见误区
KMP 已稳定,就不用做双端 PoC
稳定说明核心技术遵循相应的兼容承诺,不等于项目中的全部 SDK、插件、系统版本、Native 库、签名方式和保护策略组合都已被验证。双端 PoC 的目标正是确认当前项目选择的组合。
iOS 模拟器通过即可代表 IPA 通过
模拟器与真机 target、架构和签名环境不同。真正发布的判断对象是目标分发方式下的 Archive 与最终 IPA。
Android 构建成功即可说明 KMP Library 变体正确
新的 KMP Android Library plugin 采用单变体模型,构建成功并不会自动证明应用 Release/flavor、依赖匹配、R8 和最终 AAB 的行为正确。
为了解决兼容问题可以让供应方持有生产私钥
不需要。项目方可控制最终签名;验收应围绕候选包身份、构建条件、保护策略、关键路径和发布对象建立可追溯关系。
FAQ
KMP 项目能像普通 Android App 一样加固吗?
Android 交付部分仍需要按 Android Release、R8、ABI、SO、AAB 和最终签名包验收;但不能忽略同一共享模块在 iOS Native/XCFramework 中的另一条发布路径。KMP 项目应做双端分开记录、统一管理的 PoC。
Compose Multiplatform 是否需要额外写一套验收?
若项目共享 UI,应在共享逻辑验收之外增加页面导航、状态恢复、平台组件嵌入、系统版本和架构范围。具体最低版本与支持状态以正在使用的 Compose 版本官方兼容表为准。
KMP 使用 CocoaPods 或 Swift Package 时,检查重点不同吗?
共同重点是最终 Archive 中的真实依赖、Framework/slice、资源与签名;不同点在于依赖解析、嵌入方式和版本锁定。报告应写明接入方式,而不是只写“iOS 已集成”。
可以先只验证一个平台吗?
可以,但报告必须明确另一个平台未覆盖。先完成一端的候选对象和关键路径基线,再扩展到另一端,通常比将两个平台的异常混在一起排查更有效。
相关页面与下一步
若你正在处理 KMP 共享模块、iOS XCFramework 或 Android Release 在加固后出现的差异,可提交目标平台、当前框架版本、Release 候选对象和一条关键业务路径,先确定双端兼容性验证范围,再进入正式发布安排。