非常棒的选题!
《从 SSP 集成到 Gradle 构建:一个 Android SDK 开发者的踩坑全记录》这个主题非常典型,尤其适合 Android SDK 方向的技术博文或中高级开发者的分享文案。下面我给出一份完整的技术文章结构 + 实战细节说明,覆盖 SSP → AAR → Maven/Gradle 发布与兼容性调试 全流程,并在每一节穿插“坑点 + 解决方案”。
🧭 一、前言:为什么这一路这么坎
做 Android SDK 的人都知道:
从把一个 .jar 集成到 Demo 工程,到能优雅地通过 Gradle 引入你的 SDK,中间的坑堪比地狱副本。
而当项目中引入 SSP(Shared SDK Platform) 模块化架构后,坑的数量更是指数上升。
本文记录我在将 SSP 平台 SDK 模块迁移至 Gradle 构建体系中的完整踩坑过程,涵盖:
- SDK 模块化设计 → Gradle 多模块构建;
- AAR 打包与资源冲突;
- Maven 发布与依赖解析;
- Android Gradle Plugin(AGP)版本坑;
- 以及最关键的:如何让 SSP 模块在 Gradle 中“既能跑、又能发”。
⚙️ 二、背景:SSP 是什么,它为什么这么难搞
SSP(Shared SDK Platform)是一种 统一的 SDK 架构方案,常用于公司内部多个业务 SDK 的统一封装。
它通常包含:
- 一组核心模块(如
ssp-core,ssp-utils,ssp-analytics); - 一套基础通信框架(跨 SDK 共享的网络层、配置中心、日志收集);
- 多个业务侧 SDK 通过
compileOnly或api引用 SSP 核心模块。
📌 问题在于:
SSP 设计之初往往以“本地 module 依赖”为主,而当你要打包成单独 AAR 并发布到 Maven 仓库时:
- 模块间依赖错综复杂;
- 资源命名冲突;
- Gradle Plugin 版本不兼容;
- SSP 内部的构建脚本往往不适配外部环境。
🧩 三、迁移之路第一步:Gradle 多模块拆解
✅ 目标
将原本单体的 SSP SDK(包含数十个模块)逐步迁移到标准 Gradle 构建体系中。
⚠️ 坑点 1:模块互相引用 + api/implementation 混乱
原 SSP 工程里常见这种依赖:
dependencies {
api project(':ssp-core')
api project(':ssp-analytics')
}
但当这些模块要被打包为单独 AAR 时,Gradle 会提示:
“Cannot resolve project dependencies when publishing to Maven”
解决:
- 把
api project(':xxx')改为api 'com.company.ssp:xxx:1.0.0' - 统一在
settings.gradle里用includeBuild或composite build模式调试
(这样在本地仍能源码依赖,发布时又能走 Maven)
🧱 四、打包 AAR:从 assembleRelease 到自定义 Task
⚠️ 坑点 2:资源文件冲突
多个 SSP 模块各自带有 /res/values/strings.xml 与 /res/drawable/ic_logo.xml,结果在打包时出现:
Duplicate resources detected: res/values/strings.xml
解决:
- 在 SSP 核心模块中统一
resPrefix,例如:android { resourcePrefix "ssp_" } - 对外暴露资源时,定义公共前缀(如
ssp_common_)避免冲突; - 对业务 SDK 的资源则使用
consumerProguardFiles控制可见性。
⚠️ 坑点 3:R 文件合并失败
由于 SSP 模块使用了 android.enableAapt2=true(新 Gradle 默认开启),老版本 SSP 的 R.java 静态引用方式报错:
symbol not found for R.string.xxx
解决:
- 不再直接引用
com.ssp.core.R.xxx; - 使用
context.getResources().getIdentifier()或 Resource Binding; - 或升级 AGP 版本 ≥ 7.0 并启用命名空间:
android { namespace "com.ssp.core" }
📦 五、发布到 Maven:从 maven-publish 到自建仓库
⚠️ 坑点 4:Gradle Plugin 版本不兼容
SSP 工程中原用 com.github.dcendents.android-maven,在 Gradle 7+ 已弃用。
解决:使用官方 maven-publish 插件
publishing {
publications {
release(MavenPublication) {
from components.release
groupId = 'com.company.ssp'
artifactId = 'ssp-core'
version = '1.0.0'
}
}
}
⚠️ 坑点 5:AAR 无法携带依赖
默认 AAR 发布不会包含 transitive dependencies。
解决:
添加自定义 dependencies 块:
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.implementation.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
🧰 六、测试与验证:Demo 工程的噩梦调试
⚠️ 坑点 6:SSP 初始化失败(ClassNotFound)
AAR 被引用后运行时报:
java.lang.ClassNotFoundException: com.ssp.core.SSPManager
原因:AAR 被打包但 proguard 混淆掉核心类。
解决:
- 在
consumer-proguard-rules.pro中添加:-keep class com.ssp.** { *; } - 在发布脚本中声明:
consumerProguardFiles 'consumer-rules.pro'
🚀 七、最终架构与构建流程总结
.
├── ssp-core/ # 核心逻辑
├── ssp-analytics/ # 数据统计
├── ssp-utils/ # 工具模块
├── sdk-demo/ # 测试 Demo
├── build.gradle # 顶层构建
└── settings.gradle # includeBuild 管理
发布流程:
- 本地调试:
./gradlew :ssp-core:assembleRelease - 校验依赖:
./gradlew dependencies - 发布 Maven:
./gradlew publishReleasePublicationToMavenRepository - Demo 引用测试:
implementation 'com.company.ssp:ssp-core:1.0.0'
🧠 八、踩坑后的总结与经验沉淀
| 问题 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| AAR 资源冲突 | strings.xml 重复 | 未设置 resPrefix | 统一命名 + resourcePrefix |
| ClassNotFound | SSPManager 找不到 | 混淆掉类 | 添加 consumerProguard |
| 构建失败 | Gradle Plugin 版本不兼容 | plugin 弃用 | 用官方 maven-publish |
| 依赖丢失 | SDK 引入后功能不全 | 未声明 transitive deps | POM 手动注入依赖 |
| Demo 无法运行 | SSP 模块命名空间冲突 | namespace 未设 | Gradle 7+ 明确命名空间 |
发表回复