|
|
@@ -1,868 +0,0 @@
|
|
|
-# 能力模块开发规范
|
|
|
-
|
|
|
-## 📋 核心原则
|
|
|
-
|
|
|
-**每个能力模块必须完全独立,自己管理自己的所有配置和依赖,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
|
|
|
-<!-- 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>
|
|
|
-```
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- 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>
|
|
|
-```
|
|
|
-
|
|
|
-#### ❌ 错误做法
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- app/src/main/res/values/strings.xml -->
|
|
|
-<resources>
|
|
|
- <!-- ❌ 不要在这里放能力模块的配置! -->
|
|
|
- <string name="jpush_app_key">...</string>
|
|
|
- <string name="share_umeng_app_key">...</string>
|
|
|
-</resources>
|
|
|
-```
|
|
|
-
|
|
|
-### 2. AndroidManifest.xml 配置
|
|
|
-
|
|
|
-**每个能力模块在自己的 AndroidManifest.xml 中配置,使用自己的 `@string/` 资源**
|
|
|
-
|
|
|
-#### ✅ 正确做法
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- 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>
|
|
|
-```
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- capability-share/src/main/AndroidManifest.xml -->
|
|
|
-<activity>
|
|
|
- <!-- 使用本模块的 @string/ 资源 -->
|
|
|
- <data android:scheme="@string/qq_app_id_scheme" />
|
|
|
-</activity>
|
|
|
-```
|
|
|
-
|
|
|
-#### ❌ 错误做法
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- app/src/main/AndroidManifest.xml -->
|
|
|
-<application>
|
|
|
- <!-- ❌ 不要在这里配置能力模块的 meta-data! -->
|
|
|
- <meta-data android:name="JPUSH_APPKEY" ... />
|
|
|
-</application>
|
|
|
-```
|
|
|
-
|
|
|
-```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
|
|
|
- <!-- ❌ 禁止 -->
|
|
|
- <string name="jpush_app_key">...</string>
|
|
|
- <string name="share_umeng_app_key">...</string>
|
|
|
- ```
|
|
|
-
|
|
|
-2. **禁止在 app 模块的 `AndroidManifest.xml` 中配置能力模块的 meta-data**
|
|
|
- ```xml
|
|
|
- <!-- ❌ 禁止 -->
|
|
|
- <meta-data android:name="JPUSH_APPKEY" ... />
|
|
|
- ```
|
|
|
-
|
|
|
-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
|
|
|
-<!-- capability-new/src/main/res/values/strings.xml -->
|
|
|
-<resources>
|
|
|
- <string name="new_module_config_key">your_config_value_here</string>
|
|
|
-</resources>
|
|
|
-```
|
|
|
-
|
|
|
-### 步骤 3:在 AndroidManifest.xml 中使用
|
|
|
-
|
|
|
-```xml
|
|
|
-<!-- capability-new/src/main/AndroidManifest.xml -->
|
|
|
-<application>
|
|
|
- <meta-data
|
|
|
- android:name="NEW_MODULE_KEY"
|
|
|
- android:value="@string/new_module_config_key" />
|
|
|
-</application>
|
|
|
-```
|
|
|
-
|
|
|
-### 步骤 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
|
|
|
-<?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>
|
|
|
-```
|
|
|
-
|
|
|
-### 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年**
|
|
|
-
|