# 能力模块开发规范 ## 📋 核心原则 **每个能力模块必须完全独立,自己管理自己的所有配置和依赖,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年**