# 能力模块开发规范
## 📋 核心原则
**每个能力模块必须完全独立,自己管理自己的所有配置和依赖,app 模块只负责组装,不负责管理能力模块的内部事务。**
---
## 🎯 原始诉求
### 问题背景
- ❌ **之前的问题**:所有配置都在 app 模块的 `strings.xml` 中
- ❌ **之前的问题**:所有依赖都在 app 模块的 `build.gradle` 中重复声明
- ❌ **之前的问题**:app 模块需要解析 XML 来读取配置
- ❌ **之前的问题**:能力模块的配置散落在各处,难以维护
### 解决方案
- ✅ **现在**:每个能力模块有自己的 `strings.xml`
- ✅ **现在**:每个能力模块管理自己的依赖(使用 `api` 传递)
- ✅ **现在**:不需要 XML 解析,直接使用 `@string/` 引用
- ✅ **现在**:能力模块完全独立,app 模块只负责组装
---
## 📁 模块结构规范
### 标准目录结构
```
capability-xxx/
├── build.gradle # 模块依赖配置
├── src/main/
│ ├── AndroidManifest.xml # 模块的 AndroidManifest(包含 meta-data、Activity 等)
│ ├── res/
│ │ └── values/
│ │ └── strings.xml # 模块自己的配置(必须!)
│ └── java/... # 模块代码
└── libs/ # 模块自己的 SDK 文件(如果有)
```
---
## 🔧 配置管理规范
### 1. strings.xml 配置
**每个能力模块必须有自己的 `strings.xml`**
#### ✅ 正确做法
```xml
your_jpush_appkey_here
developer-default
```
```xml
your_umeng_appkey_here
your_wechat_appid_here
tencentyour_qq_appid_here
```
#### ❌ 错误做法
```xml
...
...
```
### 2. AndroidManifest.xml 配置
**每个能力模块在自己的 AndroidManifest.xml 中配置,使用自己的 `@string/` 资源**
#### ✅ 正确做法
```xml
```
```xml
```
#### ❌ 错误做法
```xml
```
```groovy
// app/build.gradle
// ❌ 不要在这里配置能力模块的参数!
manifestPlaceholders = [
XIAOMI_APPID: "..."
]
```
---
## 📦 依赖管理规范
### 1. 依赖声明
**每个能力模块在自己的 `build.gradle` 中声明依赖**
#### ✅ 正确做法
```groovy
// 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'))
}
```
```groovy
// app/build.gradle
dependencies {
// 只需要依赖能力模块,不需要重复声明能力模块的依赖
implementation project(':capability-share')
// ❌ 不要在这里重复声明微信 SDK!
}
```
#### ❌ 错误做法
```groovy
// app/build.gradle
dependencies {
implementation project(':capability-share')
// ❌ 不要重复声明能力模块的依赖!
implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
}
```
### 2. 依赖传递规则
| 依赖类型 | 是否传递到 app 模块 | 使用场景 |
|---------|-------------------|---------|
| `api` | ✅ 是 | 需要暴露给 app 模块使用的依赖(如 SDK) |
| `implementation` | ❌ 否 | 模块内部使用的依赖 |
**规则:如果 app 模块需要使用某个依赖,能力模块必须使用 `api` 声明**
---
## 🚫 禁止事项
### ❌ 禁止在 app 模块中做的事情
1. **禁止在 app 模块的 `strings.xml` 中配置能力模块的参数**
```xml
...
...
```
2. **禁止在 app 模块的 `AndroidManifest.xml` 中配置能力模块的 meta-data**
```xml
```
3. **禁止在 app 模块的 `build.gradle` 中重复声明能力模块的依赖**
```groovy
// ❌ 禁止
implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
```
4. **禁止在 app 模块的 `build.gradle` 中使用 XML 解析读取配置**
```groovy
// ❌ 禁止
def xml = new XmlParser().parse(resFile)
manifestPlaceholders = [...]
```
5. **禁止在 app 模块中管理能力模块的内部逻辑**
---
## ✅ 正确做法检查清单
创建新能力模块时,请按以下清单检查:
### 配置管理
- [ ] 在 `capability-xxx/src/main/res/values/strings.xml` 中创建配置
- [ ] 在 `capability-xxx/src/main/AndroidManifest.xml` 中使用 `@string/` 引用配置
- [ ] 不在 app 模块的 `strings.xml` 中添加能力模块的配置
- [ ] 不在 app 模块的 `AndroidManifest.xml` 中配置能力模块的 meta-data
### 依赖管理
- [ ] 在 `capability-xxx/build.gradle` 中声明所有依赖
- [ ] 需要暴露给 app 的依赖使用 `api`
- [ ] 模块内部依赖使用 `implementation`
- [ ] 不在 app 模块的 `build.gradle` 中重复声明能力模块的依赖
### 代码管理
- [ ] 能力模块的代码完全封装在模块内部
- [ ] 通过 Factory 模式暴露接口给 app 模块
- [ ] app 模块只调用能力模块的公开接口
---
## 📝 示例:创建新能力模块
### 步骤 1:创建模块结构
```
capability-new/
├── build.gradle
├── src/main/
│ ├── AndroidManifest.xml
│ ├── res/values/strings.xml
│ └── java/...
└── libs/(如果需要)
```
### 步骤 2:创建 strings.xml
```xml
your_config_value_here
```
### 步骤 3:在 AndroidManifest.xml 中使用
```xml
```
### 步骤 4:在 build.gradle 中声明依赖
```groovy
// capability-new/build.gradle
dependencies {
// 需要暴露给 app 的依赖使用 api
api("com.example:sdk:1.0.0")
// 模块内部依赖使用 implementation
implementation("com.example:internal-lib:1.0.0")
}
```
### 步骤 5:在 app 模块中引用
```groovy
// app/build.gradle
dependencies {
// 只需要依赖能力模块,不需要重复声明能力模块的依赖
implementation project(':capability-new')
}
```
---
## 🔍 常见问题
### Q1: 为什么能力模块的依赖要用 `api` 而不是 `implementation`?
**A:** 因为 app 模块在运行时需要这些依赖(如微信 SDK),如果使用 `implementation`,依赖不会传递到 app 模块,会导致运行时找不到类。
### Q2: 能力模块的 `strings.xml` 中的资源,app 模块能访问吗?
**A:** 可以。Android 构建系统会合并所有模块的资源,app 模块可以通过 `context.resources.getIdentifier()` 访问能力模块的资源。
### Q3: 如果能力模块需要读取配置,应该怎么做?
**A:** 在能力模块的代码中使用 Android 标准的资源 API:
```kotlin
// 在能力模块的代码中
val resources = context.resources
val resId = resources.getIdentifier("config_key", "string", context.packageName)
val value = resources.getString(resId)
```
**不要使用 XML 解析!**
### Q4: app 模块的 `strings.xml` 应该放什么?
**A:** 只放 app 模块自己的配置,如:
- `app_name`
- app 模块特有的配置
**不要放能力模块的配置!**
---
## 📚 参考
### 当前项目中的正确示例
1. **capability-push 模块**
- ✅ 配置:`capability-push/src/main/res/values/strings.xml`
- ✅ AndroidManifest:`capability-push/src/main/AndroidManifest.xml`
- ✅ 依赖:`capability-push/build.gradle`
2. **capability-share 模块**
- ✅ 配置:`capability-share/src/main/res/values/strings.xml`
- ✅ AndroidManifest:`capability-share/src/main/AndroidManifest.xml`
- ✅ 依赖:`capability-share/build.gradle`(使用 `api` 传递依赖)
---
## 🎯 总结
**记住一句话:每个能力模块都是独立的,自己管理自己的所有事情,app 模块只负责组装,不负责管理!**
- ✅ 配置 → 能力模块的 `strings.xml`
- ✅ AndroidManifest → 能力模块的 `AndroidManifest.xml`
- ✅ 依赖 → 能力模块的 `build.gradle`(使用 `api` 传递)
- ✅ 代码 → 能力模块的 `java/` 目录
**app 模块只做一件事:`implementation project(':capability-xxx')`**
---
---
## 💻 代码开发规范
### 1. 接口设计规范
#### ✅ 必须定义接口(API)
每个能力模块必须定义清晰的接口,实现类封装在内部:
```kotlin
// capability-xxx/src/main/java/.../api/XxxService.kt
interface XxxService {
fun initialize(context: Context, config: XxxConfig)
fun doSomething(): XxxResponse
// ...
}
```
#### ✅ 实现类使用 internal 或 private
```kotlin
// capability-xxx/src/main/java/.../impl/XxxServiceImpl.kt
internal class XxxServiceImpl : XxxService {
// 实现细节
}
```
#### ❌ 禁止直接暴露实现类
```kotlin
// ❌ 禁止
class XxxServiceImpl : XxxService { ... } // 不要用 public
```
### 2. Factory 模式规范
**每个能力模块必须使用 Factory 模式提供单例服务**
#### ✅ 标准 Factory 结构
```kotlin
// 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()"
)
}
}
```
#### ✅ 使用示例
```kotlin
// 在 Application 中初始化
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
XxxServiceFactory.init(applicationContext)
}
}
// 在业务代码中使用
val service = XxxServiceFactory.getInstance()
service.doSomething()
```
### 3. 配置类设计规范
#### ✅ 配置类必须支持从资源文件读取
```kotlin
// 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 解析
```kotlin
// ❌ 禁止在代码中解析 XML
val xml = XmlParser().parse(file)
```
### 4. 响应模型规范
#### ✅ 统一的响应模型
```kotlin
// 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()
)
```
### 5. 错误处理规范
#### ✅ 必须处理异常,返回统一的响应
```kotlin
override fun doSomething(): XxxResponse {
return try {
// 业务逻辑
XxxResponse(success = true, data = result)
} catch (e: Exception) {
ILog.e(tag, "操作失败", e)
XxxResponse(
success = false,
errorMessage = e.message ?: "未知错误"
)
}
}
```
#### ❌ 禁止抛出未处理的异常
```kotlin
// ❌ 禁止
override fun doSomething() {
throw Exception("错误") // 必须捕获并返回 Response
}
```
---
## 📝 日志规范
### 1. 使用统一的日志接口
**所有能力模块必须使用 `base-core` 模块的 `ILog` 接口**
#### ✅ 正确做法
```kotlin
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)
}
}
}
```
#### ❌ 禁止直接使用 Android Log
```kotlin
// ❌ 禁止
import android.util.Log
Log.d("Tag", "message")
```
### 2. 日志级别规范
| 级别 | 使用场景 | 示例 |
|------|---------|------|
| `ILog.d()` | 调试信息、流程跟踪 | "开始初始化"、"收到回调" |
| `ILog.i()` | 重要信息 | "服务初始化成功" |
| `ILog.w()` | 警告信息 | "配置缺失,使用默认值" |
| `ILog.e()` | 错误信息(必须带异常) | "初始化失败", exception |
### 3. Tag 命名规范
```kotlin
// ✅ 使用类名或模块名
private val tag = "XxxService"
private val tag = "XxxServiceImpl"
private val tag = "XxxReceiver"
```
---
## 🔨 编译配置规范
### 1. build.gradle 配置
#### ✅ 标准配置结构
```groovy
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')
}
```
#### ❌ 禁止事项
```groovy
// ❌ 禁止使用 application 插件
plugins {
id("com.android.application") // 能力模块必须是 library
}
// ❌ 禁止在能力模块中配置 applicationId
defaultConfig {
applicationId "..." // 只有 app 模块才有
}
```
### 2. AndroidManifest.xml 配置
#### ✅ 标准结构
```xml
```
### 3. ProGuard 规则
**如果模块使用了第三方 SDK,必须在模块的 `proguard-rules.pro` 中添加规则**
```proguard
# capability-xxx/proguard-rules.pro
-keep class com.example.sdk.** { *; }
-dontwarn com.example.sdk.**
```
---
## 🧪 测试规范
### 1. 单元测试
**每个能力模块应该有自己的单元测试**
```
capability-xxx/
└── src/test/
└── java/.../XxxServiceTest.kt
```
### 2. 集成测试
**在 app 模块中进行集成测试**
```
app/
└── src/androidTest/
└── java/.../XxxServiceIntegrationTest.kt
```
---
## 📦 版本管理规范
### 1. SDK 版本管理
**所有 SDK 版本统一在 `gradle/libs.versions.toml` 中管理**
```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" }
```
### 2. 模块版本号
**能力模块不需要版本号(因为是内部模块)**
```groovy
// ❌ 不需要
version = "1.0.0"
```
---
## 🔍 代码审查检查清单
### 创建新能力模块时检查
- [ ] **模块结构**
- [ ] 有 `strings.xml` 配置文件
- [ ] 有 `AndroidManifest.xml`
- [ ] 有 `build.gradle`(使用 `library` 插件)
- [ ] **接口设计**
- [ ] 定义了清晰的 API 接口
- [ ] 实现类使用 `internal` 或 `private`
- [ ] 使用 Factory 模式提供单例
- [ ] **配置管理**
- [ ] 配置在模块自己的 `strings.xml` 中
- [ ] AndroidManifest 使用 `@string/` 引用
- [ ] 配置类支持从资源文件读取
- [ ] **依赖管理**
- [ ] 依赖在模块自己的 `build.gradle` 中
- [ ] 需要暴露的依赖使用 `api`
- [ ] app 模块不重复声明依赖
- [ ] **日志规范**
- [ ] 使用 `ILog` 接口
- [ ] 不使用 `android.util.Log`
- [ ] Tag 命名规范
- [ ] **错误处理**
- [ ] 所有异常都被捕获
- [ ] 返回统一的 Response 模型
- [ ] 不抛出未处理的异常
---
## 🚨 常见错误和解决方案
### 错误 1:运行时找不到类
**原因**:依赖使用了 `implementation` 而不是 `api`
**解决**:
```groovy
// 改为 api
api("com.example:sdk:1.0.0")
```
### 错误 2:找不到资源
**原因**:在 app 模块的 `strings.xml` 中配置了能力模块的资源
**解决**:移到能力模块自己的 `strings.xml` 中
### 错误 3:编译错误:unable to resolve class
**原因**:依赖没有正确传递
**解决**:
1. 检查能力模块的依赖是否使用 `api`
2. 检查 app 模块是否正确依赖了能力模块
### 错误 4:配置读取失败
**原因**:使用了 XML 解析而不是 Android 资源 API
**解决**:
```kotlin
// ✅ 正确
val resId = resources.getIdentifier("key", "string", packageName)
val value = resources.getString(resId)
// ❌ 错误
val xml = XmlParser().parse(file)
```
---
## 📚 参考示例
### 当前项目中的标准实现
1. **capability-push 模块**
- ✅ Factory: `PushServiceFactory`
- ✅ API: `PushService`
- ✅ Config: `PushConfig.fromResources()`
- ✅ 配置: `capability-push/src/main/res/values/strings.xml`
2. **capability-share 模块**
- ✅ Factory: `ShareServiceFactory`
- ✅ API: `ShareService`
- ✅ Config: `ShareConfig.fromResources()`
- ✅ 配置: `capability-share/src/main/res/values/strings.xml`
---
## 🎯 总结
### 核心原则(再次强调)
1. **独立性**:每个能力模块完全独立,不依赖 app 模块
2. **封装性**:实现细节封装在模块内部,只暴露接口
3. **统一性**:使用统一的 Factory 模式、日志接口、响应模型
4. **规范性**:遵循统一的代码规范和目录结构
### 开发流程
1. 创建模块目录结构
2. 定义 API 接口
3. 实现服务类(internal)
4. 创建 Factory(单例)
5. 创建配置类(支持从资源读取)
6. 创建 `strings.xml` 配置文件
7. 配置 `AndroidManifest.xml`
8. 配置 `build.gradle`(依赖使用 `api`)
9. 在 app 模块中依赖:`implementation project(':capability-xxx')`
10. 在 Application 中初始化:`XxxServiceFactory.init(context)`
---
**最后更新:2024年**