Proton Pass如何用Kotlin调用Rust?common-rust JNI桥接层完整剖析
Proton Pass如何用Kotlin调用Rustcommon-rust JNI桥接层完整剖析【免费下载链接】android-passAndroid code for the Proton Pass application项目地址: https://gitcode.com/gh_mirrors/an/android-passProton Pass 是一款由 Proton 开源的跨平台密码管理器Password ManagerAndroid 客户端。它的界面层用 Kotlin Jetpack Compose 编写而密码生成、强度评估等密码学核心逻辑则跑在 Rust 编写的proton-pass-common库里。两者之间靠一层 JNIJava Native Interface桥接沟通。本文带你完整剖析pass/common-rust模块看懂 Kotlin 是如何一步步调用 Rust 的。一、为什么密码管理器要混用 Kotlin 和 Rust很多安全类应用都有同样的架构诉求UI 层Kotlin 生态成熟Compose 让界面开发效率很高核心层密码生成、口令短语passphrase、密钥运算等逻辑需要内存安全、可跨 Android / iOS / 桌面端复用的实现Rust 是理想选择。Proton 的方案是把核心逻辑放在独立的 Rust 仓库proton-pass-common中编译成各平台的原生库再发布为 Maven 工件供各端消费。Android 端就是本文的主角——pass/common-rust模块。二、common-rust 模块的三段式结构pass/common-rust/下有三个子模块是典型的“接口 / 实现 / 假实现”分层子模块角色关键说明pass/common-rust/api/纯 Kotlin 接口层只用org.jetbrains.kotlin.jvm插件定义EmailValidator、PasswordGenerator等接口任何模块都只依赖它pass/common-rust/impl/Rust 桥接实现层Android 库模块依赖 Rust 库并实现全部接口pass/common-rust/fakes/测试替身层提供FakeEmailValidator等假实现供 UI 测试与演示模式使用接口定义极其简洁例如邮箱校验接口只有两个方法签名// pass/common-rust/api/src/main/kotlin/proton/android/pass/commonrust/api/EmailValidator.kt interface EmailValidator { fun isValid(email: String): Boolean }三、Rust 库如何进入 Kotlinpass-common 依赖Rust 编译产物通过 Maven 工件接入在gradle/libs.versions.toml中声明passCommon 1.7.1 pass-common { module me.proton.pass.common:lib, version.ref passCommon }pass/common-rust/impl/build.gradle.kts中一行implementation(libs.pass.common)就把它引入了。这个 AAR 里包含由 Rust 编译出的.so文件以及 Rust 通过android_system_properties等机制暴露给 Kotlin 的类比如RandomPasswordGenerator/PassphraseGenerator随机密码与口令短语生成器SshKeyManagerSSH 密钥管理libraryVersion()返回 Rust 库版本号另外settings.gradle.kts约 135–144 行内置了本地 composite build 支持开发者修改 Rust 代码时把pass.common.local.path指向本地proton-pass-common仓库Gradle 会直接用本地构建替换远程工件无需等待发布。Rust 类还要在混淆时保留app/proguard/proton-pass.pro第 13 行专门写了一条规则-keep class proton.android.pass.commonrust.** { *; }四、Kotlin 调 Rust 的三种典型形态翻遍pass/common-rust/impl/src/main/kotlin/proton/android/pass/commonrust/impl/目录Kotlin 调用 Rust 基本就这三种姿势。1. 直接持有 Rust 对象用 Hilt 提供Rust 库里暴露的生成器类本身就是“无状态对象”Kotlin 直接new出来交给依赖注入容器即可。CommonRustModule.ktpass/common-rust/impl/src/main/kotlin/proton/android/pass/commonrust/impl/CommonRustModule.kt里的companion object部分[Provides Singleton] fun provideRandomPasswordGenerator() RandomPasswordGenerator() [Provides Singleton] fun provideSshKeyManager() SshKeyManager()这里RandomPasswordGenerator、SshKeyManager都是 Rust 侧定义的类Kotlin 像用普通类一样使用它们——JNI 细节被完全封装在 AAR 内部。2. Kotlin 配置对象 → Rust 配置对象的映射更常见的场景是Kotlin 侧先把自己的配置转换成 Rust 配置再调用 Rust 方法。看密码生成器PasswordGeneratorImpl.ktoverride suspend fun generatePassword(config: PasswordConfig): String withContext(appDispatchers.default) { when (config) { is PasswordConfig.Random - RandomPasswordConfig( length config.length.toUInt(), numbers config.includeNumbers, ... ).let(randomPasswordGenerator::generate) is PasswordConfig.Memorable - PassphraseConfig( separator config.wordSeparator.asRustWordSeparator(), ... ).let(passphraseGenerator::generateRandomPassphrase) } }注意两个细节withContext(appDispatchers.default)把 JNI 调用切到后台线程避免阻塞 UI 主线程asRustWordSeparator()把 Kotlin 枚举PasswordWordSeparator.Hyphen一一映射为 Rust 枚举WordSeparator.HYPHENS——两侧各自维护自己的类型系统桥接层负责翻译这是 JNI 桥接最标准的写法。3. 直接调用 Rust 静态函数最简单的形态是调一个 Rust 函数取个值。CommonLibraryVersionCheckerImpl.kt全文核心就一行override fun getVersion() libraryVersion()libraryVersion()是 Rust 侧暴露的函数App 用它来做 Rust 核心库版本上报与自检。五、Hilt 依赖注入接口与实现彻底解耦CommonRustModule.kt中大量Binds声明把每个接口的 Rust 桥接实现绑定进SingletonComponent[Binds Singleton] abstract fun bindPasswordGenerator(impl: PasswordGeneratorImpl): PasswordGenerator [Binds Singleton] abstract fun bindPasswordStrengthCalculator(impl: RustPasswordStrengthCalculator): PasswordStrengthCalculator业务代码只依赖api模块的接口根本不知道 Rust 的存在。而在单元测试和 Compose UI 测试中pass/common-rust/fakes/里的FakesCommonRustModule会整体替换成假实现比如FakeEmailValidator提供一个setResult(value)开关让测试随时控制“邮箱是否合法”override fun isValid(email: String): Boolean result同一套接口生产走 Rust测试走 Fake——这是该桥接层可测试性的关键设计。六、桥接层都覆盖了哪些功能api模块下的 17 个接口就是 Proton Pass 对 Rust 核心库的全部“诉求清单”密码类PasswordGenerator随机密码 / 口令短语、PasswordScorer密码评分、PasswordStrengthCalculator强度计算含RustPasswordPenaltyMapper把 Rust 的惩罚项映射回 Kotlin 模型用户名与邮箱UsernameGenerator、EmailValidator、DomainManager域名管理、AliasPrefixValidator别名前缀校验密钥与签名SshKeyGeneratorSSH 密钥、NewUserInviteSignatureBodyCreator新用户邀请签名体工具类WifiNetworkQRGeneratorWiFi 配置二维码、FileTypeDetector文件类型嗅探、UsableShareFilter共享过滤、CommonLibraryVersionChecker版本检查这些能力最终服务于 App 的核心场景自动填充、密码共享、安全中心提示等例如共享联系人创建流程中的界面七、小结一条清晰的阅读路径想深入这条 Kotlin → JNI → Rust 的调用链建议按这个顺序读源码pass/common-rust/api/—— 先看接口弄清 App 需要 Rust 做什么pass/common-rust/impl/—— 再看每种“翻译 调用”模式PasswordGeneratorImpl.kt是最佳范本;pass/common-rust/impl/.../CommonRustModule.kt—— 看 Hilt 如何把所有实现装配起来;gradle/libs.versions.toml中的passCommon与settings.gradle.kts的 composite build 逻辑 —— 搞清 Rust 工件从哪来、本地如何替换。整套设计值得借鉴的要点是接口先行、实现隔离、测试可替换、JNI 细节零泄露。Kotlin 开发者感知不到.so文件的存在而 Rust 工程师也感知不到 Compose 的存在——两层语言通过一个干净的桥接模块井水不犯河水这正是多语言混合架构的理想状态。✅【免费下载链接】android-passAndroid code for the Proton Pass application项目地址: https://gitcode.com/gh_mirrors/an/android-pass创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考