Android 设备的容量、网速、处理能力都在飞速发展,现在不止是很多 app 体积变大,其实不少 SDK 多年沉淀下来后其体积也不容小觑。在构建一个中大型 SDK 时,管理其内部复杂性、确保代码质量和开发效率至关重要。此时,引入一个灵活小巧的嵌入式依赖注入框架,例如 Koin,就显得尤为关键。它能有效地帮助团队:
- 促进模块解耦: 将庞大的 SDK 拆分为低耦合、职责单一的模块,使整体结构更清晰、易于维护和迭代。
- 增强代码可测性: 通过注入依赖,可以轻松替换实现进行单元测试和集成测试,保障 SDK 核心逻辑的稳定可靠。
- 简化维护与扩展: 明确的依赖关系使得代码更易理解,修改或添加新功能时能有效控制影响范围,降低风险。
- 支持并行开发: 基于接口和依赖注入,不同开发者或团队可以更独立地进行模块开发与测试,提升整体协作效率。
现实中,诸如地图导航、移动支付等复杂 SDK 的背后,往往是数十人规模的开发团队在协作。同时,Kotlin Multiplatform (KMP) 技术正经历快速增长,使得构建跨平台 SDK 日益成为主流选择,允许用一套代码服务于 Android、iOS 等多个平台,这进一步增加了对高质量架构的需求。而 Koin 以其简洁的 DSL 和对 Kotlin 及 KMP 的良好支持,成为了应对这些复杂场景的得力工具。它不仅能支撑大型团队在单一平台(如 Android)进行高效的模块化开发,也能为 KMP SDK 提供一致的跨平台依赖管理方案,有效管理组件间的依赖关系。
要让 SDK 真正成为一个稳定、独立的组件并实现无缝集成,其内部依赖的有效管理和与宿主环境的严格隔离就显得至关重要。而当 SDK 需要对外分发时,如何处理其内部使用的、且宿主应用也可能使用的共享依赖(比如 Koin 自身),就成了确保这种独立性的一个核心挑战。若处理不当,标准的依赖方式可能导致难以预料的类路径冲突问题。
针对这一挑战,Koin 官方团队推出了 Koin Embedded。该方案专为 Android/KMP SDK 及库的开发者设计,核心在于让 SDK 能够在内部运行一个安全、独立的 Koin 容器来管理自身依赖。通过这种隔离机制,SDK 内嵌的 Koin 环境与其宿主应用使用的任何 Koin 版本完全分离,从而有效避免了冲突。
核心机制:包重定位
Koin Embedded 实现这种隔离的核心方法,正是其运用的包重定位(Package Relocation,常称为 "Shading")技术。在编译和打包 SDK 时,通过特定脚本将 SDK 内部使用的 Koin 库的所有类的包名,从标准的 org.koin.* 替换为一个 SDK 专属且隔离的命名空间,例如 embedded.koin.*。
这样对于 JVM 或 Android 运行时来说,即使宿主 App 和 SDK 使用的 Koin 基于同一版本源码,宿主引用的 org.koin.core.KoinApplication 与 SDK 内部引用的 embedded.koin.core.KoinApplication 也会被视为两个完全不同的类。因为它们存在于各自独立的包名空间中,类路径冲突问题便迎刃而解,使得 SDK 可以使用并管理自己的 Koin 版本,无需担心对宿主 App 造成干扰。
使用方法
Koin Embedded 可以通过以下两种方式在项目中使用:
- 方式一:使用预构建的 Embedded 版本
- 直接通过 Maven 依赖引入预先构建好的版本。但包名固定为 embedded.koin.*,并仅有 3.5.6 的 core 和 android 模块
- 方式二:使用重定位脚本手动构建 (推荐)
- 手动配置脚本指定 Koin 版本、自定义包名前缀并自行构建,这个方式也是更推荐的定制化方案
方式一:使用预构建的 Embedded 版本
- 添加 Maven 仓库:
在项目的 settings.gradle 的 repositories 代码块中,添加 Kotzilla 的 Maven 仓库地址
repositories {
maven { url = uri("https://repository.kotzilla.io/repository/kotzilla-platform/") }
}
- 添加依赖:
像添加普通依赖一样,在你的 SDK 或库模块的 dependencies 代码块中添加所需的预构建 Koin Embedded 模块。目前可用包有:
- io.insert-koin:embedded-koin-core:<version>
- io.insert-koin:embedded-koin-android:<version>
- 在代码中使用
// 注意 import 路径
import embedded.koin.dsl.module
import embedded.koin.core.module.dsl.singleOf
// SDK 内部的 Koin 模块
val sdkInternalModule = module {
singleOf(::MySdkInternalService)
singleOf(::AnotherSdkComponent)
//... 其他 SDK 内部依赖
}
方式二:使用重定位脚本手动构建 (推荐)
这是推荐的做法,因为它能更好地满足定制化需求并避免潜在的冲突。
- 克隆 Koin Embedded 仓库:
获取包含所有脚本的项目文件。
git clone https://github.com/InsertKoinIO/koin-embedded.git
cd koin-embedded
- 配置 relocate.properties 文件:
打开此文件,并根据你的具体需求修改参数:
- RELOCATION_PREFIX: 设置你自己的包名前缀 (例如 mylib.koin 或 sdk_vendor.koin)。
- TARGET_KOIN_VERSION: 指定你想嵌入的 Koin 版本 (例如 3.5.6, 4.0.4 或其他 Koin 发布版本)。
- KOIN_MODULES: 列出需要重定位的 Koin 模块,用分号分隔 (例如 core/koin-core;android/koin-android)。请确保列出的模块在你指定的 Koin 版本中存在。
- BUILD_DIR: 设置构建产物(.aar/.jar)的输出目录。
- 配置 repository.gradle 文件:
如果你想将构建好的自定义 Koin 库发布到你自己的 Maven 仓库,编辑此文件并填入你的仓库信息。
repositories {
maven {
name = "MyRepository"
url = uri("https://repo.url.com")
credentials {
username = findProperty("USER")?.toString() ?: System.getenv("USER") ?: ""
password = findProperty("PWD")?.toString() ?: System.getenv("PWD") ?: ""
}
}
}
- 运行脚本:
确保你已安装 JDK 17 环境。然后在项目根目录运行
./relocate.sh
- 获取和使用构件:
- 脚本执行成功后,所有经过重定位和重新打包的 Koin 模块构件(.aar 或 .jar 文件)会出现在你在 relocate.properties 文件中通过 BUILD_DIR 指定的目录下(默认为 ./build)。
- 你可以将这些生成的构件文件直接添加到你的 SDK 或库项目的依赖中。
- 同时,这些重定位后的 Koin 模块也会被安装到你本地的 Maven 仓库 (通常是 ~/.m2/repository)。
- 如果你配置了远程 Maven 仓库(步骤 3),你可以进入 <relocation-dir>/projects 目录,并运行相应的 Gradle 发布任务(例如,如果你的仓库名为 MyRepository,则运行 publishAllPublicationsToMyRepositoryRepository)来将这些构件发布到你的远程仓库。
Koin Embedded 借助核心的包重定位技术,为 Android/KMP SDK 和库开发者提供了一个解决版本冲突、实现环境隔离的有效方案。
这种隔离机制对于确保 SDK 按照预期独立工作至关重要。它能有效防止 SDK 内部使用的 Koin 类与宿主环境可能存在的同名 Koin 类发生类路径冲突,从而保障了 SDK 内部依赖注入容器的配置和生命周期能够独立、正确地运行。这样一来,开发者便可以在 SDK 内部放心地运用 Koin 来实践模块化设计、提升代码的可测试性、支持团队并行协作以及进行 KMP 跨平台开发。
相关链接
- Koin Embedded 官方文档
https://insert-koin.io/docs/support/embedded - Koin Embedded 中文文档(点击“阅读原文”跳转)
https://koin.openaidoc.org/support/embedded - Koin Embedded 项目链接
https://github.com/InsertKoinIO/koin-embedded