每个能力模块必须完全独立,自己管理自己的所有配置和依赖,app 模块只负责组装,不负责管理能力模块的内部事务。
strings.xml 中build.gradle 中重复声明strings.xmlapi 传递)@string/ 引用capability-xxx/
├── build.gradle # 模块依赖配置
├── src/main/
│ ├── AndroidManifest.xml # 模块的 AndroidManifest(包含 meta-data、Activity 等)
│ ├── res/
│ │ └── values/
│ │ └── strings.xml # 模块自己的配置(必须!)
│ └── java/... # 模块代码
└── libs/ # 模块自己的 SDK 文件(如果有)
每个能力模块必须有自己的 strings.xml
<!-- capability-push/src/main/res/values/strings.xml -->
<resources>
<!-- 极光推送配置 -->
<string name="jpush_app_key">your_jpush_appkey_here</string>
<string name="push_jpush_channel">developer-default</string>
</resources>
<!-- capability-share/src/main/res/values/strings.xml -->
<resources>
<!-- 友盟分享配置 -->
<string name="share_umeng_app_key">your_umeng_appkey_here</string>
<string name="share_wechat_app_id">your_wechat_appid_here</string>
<string name="qq_app_id_scheme">tencentyour_qq_appid_here</string>
</resources>
<!-- app/src/main/res/values/strings.xml -->
<resources>
<!-- ❌ 不要在这里放能力模块的配置! -->
<string name="jpush_app_key">...</string>
<string name="share_umeng_app_key">...</string>
</resources>
每个能力模块在自己的 AndroidManifest.xml 中配置,使用自己的 @string/ 资源
<!-- capability-push/src/main/AndroidManifest.xml -->
<application>
<!-- 使用本模块的 @string/ 资源 -->
<meta-data
android:name="JPUSH_APPKEY"
android:value="@string/jpush_app_key"
tools:replace="android:value" />
<!-- 厂商通道配置(覆盖厂商 SDK 的占位符) -->
<meta-data
android:name="XIAOMI_APPID"
android:value="@string/mi_app_id"
tools:replace="android:value" />
</application>
<!-- capability-share/src/main/AndroidManifest.xml -->
<activity>
<!-- 使用本模块的 @string/ 资源 -->
<data android:scheme="@string/qq_app_id_scheme" />
</activity>
<!-- app/src/main/AndroidManifest.xml -->
<application>
<!-- ❌ 不要在这里配置能力模块的 meta-data! -->
<meta-data android:name="JPUSH_APPKEY" ... />
</application>
// app/build.gradle
// ❌ 不要在这里配置能力模块的参数!
manifestPlaceholders = [
XIAOMI_APPID: "..."
]
每个能力模块在自己的 build.gradle 中声明依赖
// capability-share/build.gradle
dependencies {
// 使用 api 让依赖传递到 app 模块
api("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
api(files('libs/umeng-share-wechat-full-7.3.7.jar'))
}
// app/build.gradle
dependencies {
// 只需要依赖能力模块,不需要重复声明能力模块的依赖
implementation project(':capability-share')
// ❌ 不要在这里重复声明微信 SDK!
}
// app/build.gradle
dependencies {
implementation project(':capability-share')
// ❌ 不要重复声明能力模块的依赖!
implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
}
| 依赖类型 | 是否传递到 app 模块 | 使用场景 |
|---|---|---|
api |
✅ 是 | 需要暴露给 app 模块使用的依赖(如 SDK) |
implementation |
❌ 否 | 模块内部使用的依赖 |
规则:如果 app 模块需要使用某个依赖,能力模块必须使用 api 声明
禁止在 app 模块的 strings.xml 中配置能力模块的参数
<!-- ❌ 禁止 -->
<string name="jpush_app_key">...</string>
<string name="share_umeng_app_key">...</string>
禁止在 app 模块的 AndroidManifest.xml 中配置能力模块的 meta-data
<!-- ❌ 禁止 -->
<meta-data android:name="JPUSH_APPKEY" ... />
禁止在 app 模块的 build.gradle 中重复声明能力模块的依赖
// ❌ 禁止
implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
禁止在 app 模块的 build.gradle 中使用 XML 解析读取配置
// ❌ 禁止
def xml = new XmlParser().parse(resFile)
manifestPlaceholders = [...]
禁止在 app 模块中管理能力模块的内部逻辑
创建新能力模块时,请按以下清单检查:
capability-xxx/src/main/res/values/strings.xml 中创建配置capability-xxx/src/main/AndroidManifest.xml 中使用 @string/ 引用配置strings.xml 中添加能力模块的配置AndroidManifest.xml 中配置能力模块的 meta-datacapability-xxx/build.gradle 中声明所有依赖apiimplementationbuild.gradle 中重复声明能力模块的依赖capability-new/
├── build.gradle
├── src/main/
│ ├── AndroidManifest.xml
│ ├── res/values/strings.xml
│ └── java/...
└── libs/(如果需要)
<!-- capability-new/src/main/res/values/strings.xml -->
<resources>
<string name="new_module_config_key">your_config_value_here</string>
</resources>
<!-- capability-new/src/main/AndroidManifest.xml -->
<application>
<meta-data
android:name="NEW_MODULE_KEY"
android:value="@string/new_module_config_key" />
</application>
// capability-new/build.gradle
dependencies {
// 需要暴露给 app 的依赖使用 api
api("com.example:sdk:1.0.0")
// 模块内部依赖使用 implementation
implementation("com.example:internal-lib:1.0.0")
}
// app/build.gradle
dependencies {
// 只需要依赖能力模块,不需要重复声明能力模块的依赖
implementation project(':capability-new')
}
api 而不是 implementation?A: 因为 app 模块在运行时需要这些依赖(如微信 SDK),如果使用 implementation,依赖不会传递到 app 模块,会导致运行时找不到类。
strings.xml 中的资源,app 模块能访问吗?A: 可以。Android 构建系统会合并所有模块的资源,app 模块可以通过 context.resources.getIdentifier() 访问能力模块的资源。
A: 在能力模块的代码中使用 Android 标准的资源 API:
// 在能力模块的代码中
val resources = context.resources
val resId = resources.getIdentifier("config_key", "string", context.packageName)
val value = resources.getString(resId)
不要使用 XML 解析!
strings.xml 应该放什么?A: 只放 app 模块自己的配置,如:
app_name不要放能力模块的配置!
capability-push 模块
capability-push/src/main/res/values/strings.xmlcapability-push/src/main/AndroidManifest.xmlcapability-push/build.gradlecapability-share 模块
capability-share/src/main/res/values/strings.xmlcapability-share/src/main/AndroidManifest.xmlcapability-share/build.gradle(使用 api 传递依赖)记住一句话:每个能力模块都是独立的,自己管理自己的所有事情,app 模块只负责组装,不负责管理!
strings.xmlAndroidManifest.xmlbuild.gradle(使用 api 传递)java/ 目录app 模块只做一件事:implementation project(':capability-xxx')
每个能力模块必须定义清晰的接口,实现类封装在内部:
// capability-xxx/src/main/java/.../api/XxxService.kt
interface XxxService {
fun initialize(context: Context, config: XxxConfig)
fun doSomething(): XxxResponse
// ...
}
// capability-xxx/src/main/java/.../impl/XxxServiceImpl.kt
internal class XxxServiceImpl : XxxService {
// 实现细节
}
// ❌ 禁止
class XxxServiceImpl : XxxService { ... } // 不要用 public
每个能力模块必须使用 Factory 模式提供单例服务
// capability-xxx/src/main/java/.../factory/XxxServiceFactory.kt
object XxxServiceFactory {
@Volatile
private var instance: XxxService? = null
/**
* 初始化服务(全局单例)
* @param context 应用上下文
* @param config 配置(可选,默认从资源文件读取)
*/
@JvmStatic
fun init(context: Context, config: XxxConfig? = null) {
if (instance == null) {
synchronized(this) {
if (instance == null) {
val finalConfig = config ?: XxxConfig.fromResources(context)
instance = XxxServiceImpl().apply {
initialize(context, finalConfig)
}
}
}
}
}
/**
* 获取服务实例(必须在 init 之后调用)
*/
@JvmStatic
fun getInstance(): XxxService {
return instance ?: throw IllegalStateException(
"XxxService 未初始化,请先调用 XxxServiceFactory.init()"
)
}
}
// 在 Application 中初始化
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
XxxServiceFactory.init(applicationContext)
}
}
// 在业务代码中使用
val service = XxxServiceFactory.getInstance()
service.doSomething()
// capability-xxx/src/main/java/.../model/XxxConfig.kt
data class XxxConfig(
val appKey: String,
val channel: String = "developer-default"
) {
companion object {
/**
* 从资源文件读取配置
* 资源文件:capability-xxx/src/main/res/values/strings.xml
*/
@JvmStatic
fun fromResources(context: Context): XxxConfig {
val resources = context.resources
val packageName = context.packageName
fun getString(name: String): String? {
val resId = resources.getIdentifier(name, "string", packageName)
return if (resId != 0) resources.getString(resId) else null
}
val appKey = getString("xxx_app_key")
?: throw IllegalStateException("未配置 xxx_app_key")
val channel = getString("xxx_channel") ?: "developer-default"
return XxxConfig(appKey = appKey, channel = channel)
}
}
}
// ❌ 禁止在代码中解析 XML
val xml = XmlParser().parse(file)
// capability-xxx/src/main/java/.../model/XxxResponse.kt
data class XxxResponse(
val success: Boolean,
val data: Any? = null,
val errorMessage: String? = null,
val timestamp: Long = System.currentTimeMillis()
)
override fun doSomething(): XxxResponse {
return try {
// 业务逻辑
XxxResponse(success = true, data = result)
} catch (e: Exception) {
ILog.e(tag, "操作失败", e)
XxxResponse(
success = false,
errorMessage = e.message ?: "未知错误"
)
}
}
// ❌ 禁止
override fun doSomething() {
throw Exception("错误") // 必须捕获并返回 Response
}
所有能力模块必须使用 base-core 模块的 ILog 接口
import com.narutohuo.xindazhou.core.log.ILog
class XxxServiceImpl {
private val tag = "XxxService"
fun doSomething() {
ILog.d(tag, "开始执行操作")
try {
// ...
ILog.d(tag, "操作成功")
} catch (e: Exception) {
ILog.e(tag, "操作失败", e)
}
}
}
// ❌ 禁止
import android.util.Log
Log.d("Tag", "message")
| 级别 | 使用场景 | 示例 |
|---|---|---|
ILog.d() |
调试信息、流程跟踪 | "开始初始化"、"收到回调" |
ILog.i() |
重要信息 | "服务初始化成功" |
ILog.w() |
警告信息 | "配置缺失,使用默认值" |
ILog.e() |
错误信息(必须带异常) | "初始化失败", exception |
// ✅ 使用类名或模块名
private val tag = "XxxService"
private val tag = "XxxServiceImpl"
private val tag = "XxxReceiver"
plugins {
alias(libs.plugins.kotlin.android)
id("com.android.library") // 能力模块必须是 library
}
android {
namespace = "com.narutohuo.xindazhou.xxx" // 模块自己的 namespace
compileSdk = 36
defaultConfig {
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
repositories {
flatDir {
dirs 'libs' // 本地 SDK 文件
}
mavenCentral()
google()
}
dependencies {
// 1. 依赖 base-core(所有能力模块都依赖)
implementation(project(":base-core"))
// 2. AndroidX 基础依赖
implementation(libs.androidx.core.ktx)
// 3. 模块特定的依赖
// 需要暴露给 app 的依赖使用 api
api("com.example:sdk:1.0.0")
// 模块内部依赖使用 implementation
implementation("com.example:internal-lib:1.0.0")
// 4. 本地 SDK 文件
implementation(files('libs/xxx-sdk.jar'))
implementation(name: 'xxx-sdk', ext: 'aar')
}
// ❌ 禁止使用 application 插件
plugins {
id("com.android.application") // 能力模块必须是 library
}
// ❌ 禁止在能力模块中配置 applicationId
defaultConfig {
applicationId "..." // 只有 app 模块才有
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 模块需要的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application>
<!-- 模块的 Service、Receiver、Activity 等 -->
<!-- 使用本模块的 @string/ 资源 -->
<meta-data
android:name="XXX_KEY"
android:value="@string/xxx_config_key" />
</application>
</manifest>
如果模块使用了第三方 SDK,必须在模块的 proguard-rules.pro 中添加规则
# capability-xxx/proguard-rules.pro
-keep class com.example.sdk.** { *; }
-dontwarn com.example.sdk.**
每个能力模块应该有自己的单元测试
capability-xxx/
└── src/test/
└── java/.../XxxServiceTest.kt
在 app 模块中进行集成测试
app/
└── src/androidTest/
└── java/.../XxxServiceIntegrationTest.kt
所有 SDK 版本统一在 gradle/libs.versions.toml 中管理
[versions]
androidx-core = "1.12.0"
retrofit = "2.9.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
能力模块不需要版本号(因为是内部模块)
// ❌ 不需要
version = "1.0.0"
[ ] 模块结构
strings.xml 配置文件AndroidManifest.xmlbuild.gradle(使用 library 插件)[ ] 接口设计
internal 或 private[ ] 配置管理
strings.xml 中@string/ 引用[ ] 依赖管理
build.gradle 中api[ ] 日志规范
ILog 接口android.util.Log[ ] 错误处理
原因:依赖使用了 implementation 而不是 api
解决:
// 改为 api
api("com.example:sdk:1.0.0")
原因:在 app 模块的 strings.xml 中配置了能力模块的资源
解决:移到能力模块自己的 strings.xml 中
原因:依赖没有正确传递
解决:
api原因:使用了 XML 解析而不是 Android 资源 API
解决:
// ✅ 正确
val resId = resources.getIdentifier("key", "string", packageName)
val value = resources.getString(resId)
// ❌ 错误
val xml = XmlParser().parse(file)
capability-push 模块
PushServiceFactoryPushServicePushConfig.fromResources()capability-push/src/main/res/values/strings.xmlcapability-share 模块
ShareServiceFactoryShareServiceShareConfig.fromResources()capability-share/src/main/res/values/strings.xmlstrings.xml 配置文件AndroidManifest.xmlbuild.gradle(依赖使用 api)implementation project(':capability-xxx')XxxServiceFactory.init(context)最后更新:2024年