在 Android 组件化架构中,app 模块和 mainModule(主模块)的关系通常可以理解为“壳工程”与“核心业务模块”的协作关系,具体职责和依赖关系如下:
1. app 模块:壳工程(入口模块)
- 核心职责:
- 作为应用的唯一入口,负责整合所有依赖的组件(模块)。
- 管理全局配置(如
AndroidManifest、build.gradle的全局依赖)。 - 处理应用级初始化(如
Application类的初始化、第三方 SDK 的全局初始化)。 - 最终打包生成 APK。
- 关键特性:
- 通常是一个
com.android.application类型的模块。 - 直接依赖
mainModule(主模块)和其他必要的业务/功能模块(如homeModule、userModule等)。 - 代码量极少,仅包含整合逻辑,不实现具体业务。
- 通常是一个
// app/build.gradle
dependencies {
implementation project(':mainModule') // 依赖主模块
implementation project(':common') // 依赖公共基础库
implementation project(':network') // 依赖网络模块
}
2. mainModule:主模块(核心业务模块)
- 核心职责:
- 实现应用的核心业务逻辑(如首页、主要功能)。
- 可能包含跨组件的路由配置(如通过 ARouter、DeepLinkDispatch 管理页面跳转)。
- 依赖其他基础模块(如
common、network)获取通用能力。
- 关键特性:
- 通常是一个
com.android.library类型的模块(但在调试时可独立作为application运行)。 - 可独立运行:通过
build.gradle动态切换application/library模式,方便开发时单独调试。 - 不直接依赖
app模块,保持业务逻辑的内聚性。
- 通常是一个
// mainModule/build.gradle
if (isDebug.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
3. 二者关系总结
| 维度 | app 模块 | mainModule(主模块) |
|---|---|---|
| 类型 | com.android.application | com.android.library(可切换为应用) |
| 职责 | 整合所有模块,处理全局配置和打包 | 实现核心业务逻辑 |
| 代码量 | 极少(仅整合逻辑) | 较多(业务代码集中于此) |
| 依赖关系 | 依赖 mainModule 和其他模块 | 依赖基础模块(如 common、network) |
| 独立运行 | 总是作为入口 | 可独立运行(调试时) |
4. 协作流程示例
-
开发阶段:
- 开发者单独调试
mainModule:将其配置为application,直接运行并测试核心业务。 - 其他模块(如
userModule)同理,独立开发测试。
- 开发者单独调试
-
集成阶段:
app模块通过build.gradle依赖mainModule和其他模块。- 最终打包时,所有模块的代码和资源会被合并到
app模块生成的 APK 中。
-
通信机制:
- 模块间通过接口暴露 + 依赖注入(如 Dagger/Hilt)或路由框架(如 ARouter)通信,避免直接依赖。
5. 资源冲突避免
- 所有模块的资源命名需添加前缀(如
main_),防止合并时冲突:<!-- mainModule 中资源命名 --> <string name="main_home_title">Home</string>
6. 优势
- 解耦:
app模块与mainModule职责分离,便于维护。 - 独立调试:主模块可脱离
app单独运行,提升开发效率。 - 动态组合:通过配置不同的依赖,快速生成不同变体应用(如免费版/付费版)。
通过这种设计,app 模块作为“壳”负责整合和打包,而 mainModule 作为核心业务模块专注于功能实现,共同实现组件化的核心目标:高内聚、低耦合。
在 Android 组件化工程中,app 模块作为壳工程,需要通过以下步骤在应用启动时调用 mainModule 中的首页 Activity:
**app调用mainModule的设置
步骤 1:确保 mainModule 中的首页 Activity 已正确定义
-
在
mainModule中定义首页 Activity(例如HomeActivity)并实现业务逻辑。 -
声明
HomeActivity:在mainModule的AndroidManifest.xml中声明该 Activity,但不设置LAUNCHER属性(仅在独立调试时设置)。<!-- mainModule/AndroidManifest.xml --> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".HomeActivity" android:exported="true"> <!-- 注意:此处不设置 LAUNCHER 的 intent-filter --> </activity> </application> </manifest>
步骤 2:在 app 模块中声明启动 Activity
在 app 模块的 AndroidManifest.xml 中,显式声明 HomeActivity 为启动入口,并添加 LAUNCHER 的 intent-filter:
<!-- app/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- 指定 HomeActivity 为启动页 -->
<activity
android:name="com.mainmodule.HomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
步骤 3:确保 app 模块依赖 mainModule
在 app 模块的 build.gradle 中添加对 mainModule 的依赖:
// app/build.gradle
dependencies {
implementation project(':mainModule') // 依赖主模块
}
步骤 4:处理模块间的清单合并问题
-
清单合并规则:
当mainModule作为库模块时,它的AndroidManifest.xml会与app模块的清单合并。确保app模块的清单中定义的HomeActivity的android:name路径正确(需包含mainModule的包名)。 -
解决资源冲突:
如果mainModule的资源(如布局、字符串)与app模块冲突,需在mainModule的build.gradle中配置资源前缀:// mainModule/build.gradle android { resourcePrefix "main_" }
步骤 5:独立调试配置(可选)
若希望 mainModule 在开发阶段能独立运行(无需依赖 app 模块):
- 在
mainModule的build.gradle中动态切换模块类型:// mainModule/build.gradle if (isDebug.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } - 在
mainModule的src/debug/AndroidManifest.xml中添加LAUNCHER的 intent-filter:<!-- mainModule/src/debug/AndroidManifest.xml --> <activity android:name=".HomeActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
最终效果
- 集成打包时:
app模块的清单中定义的HomeActivity会成为应用的唯一入口。 - 独立调试时:
mainModule可单独运行,直接启动HomeActivity。
常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Activity 未找到 | 1. app 模块未正确依赖 mainModule。2. Activity 类名路径错误。 | 1. 检查 build.gradle 依赖。2. 确认清单中的 android:name 正确。 |
| 启动时弹出选择器 | 多个模块声明了 LAUNCHER 的 Activity。 | 确保只有 app 模块的清单中设置了 LAUNCHER。 |
| 资源冲突 | 不同模块的资源命名重复。 | 在模块中配置 resourcePrefix。 |
高级方案:通过路由框架解耦
若希望彻底解耦模块间的直接依赖,可使用路由框架(如 ARouter):
- 在
mainModule中注册页面:@Route(path = "/main/home") public class HomeActivity extends AppCompatActivity { ... } - 在
app模块中初始化路由:public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); ARouter.init(this); } } - 通过路由路径跳转:
ARouter.getInstance().build("/main/home").navigation();
这种方式无需在 app 模块中直接依赖 mainModule,适合大型组件化工程。