Android组件化工程中app 模块和 mainModule(主模块)之间的关系

348 阅读5分钟

在 Android 组件化架构中,app 模块mainModule(主模块)的关系通常可以理解为“壳工程”与“核心业务模块”的协作关系,具体职责和依赖关系如下:


1. app 模块:壳工程(入口模块)

  • 核心职责
    • 作为应用的唯一入口,负责整合所有依赖的组件(模块)。
    • 管理全局配置(如 AndroidManifestbuild.gradle 的全局依赖)。
    • 处理应用级初始化(如 Application 类的初始化、第三方 SDK 的全局初始化)。
    • 最终打包生成 APK。
  • 关键特性
    • 通常是一个 com.android.application 类型的模块。
    • 直接依赖 mainModule(主模块)和其他必要的业务/功能模块(如 homeModuleuserModule 等)。
    • 代码量极少,仅包含整合逻辑,不实现具体业务
// app/build.gradle
dependencies {
    implementation project(':mainModule')     // 依赖主模块
    implementation project(':common')        // 依赖公共基础库
    implementation project(':network')       // 依赖网络模块
}

2. mainModule:主模块(核心业务模块)

  • 核心职责
    • 实现应用的核心业务逻辑(如首页、主要功能)。
    • 可能包含跨组件的路由配置(如通过 ARouter、DeepLinkDispatch 管理页面跳转)。
    • 依赖其他基础模块(如 commonnetwork)获取通用能力。
  • 关键特性
    • 通常是一个 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.applicationcom.android.library(可切换为应用)
职责整合所有模块,处理全局配置和打包实现核心业务逻辑
代码量极少(仅整合逻辑)较多(业务代码集中于此)
依赖关系依赖 mainModule 和其他模块依赖基础模块(如 commonnetwork
独立运行总是作为入口可独立运行(调试时)

4. 协作流程示例

  1. 开发阶段

    • 开发者单独调试 mainModule:将其配置为 application,直接运行并测试核心业务。
    • 其他模块(如 userModule)同理,独立开发测试。
  2. 集成阶段

    • app 模块通过 build.gradle 依赖 mainModule 和其他模块。
    • 最终打包时,所有模块的代码和资源会被合并到 app 模块生成的 APK 中。
  3. 通信机制

    • 模块间通过接口暴露 + 依赖注入(如 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 已正确定义

  1. mainModule 中定义首页 Activity(例如 HomeActivity)并实现业务逻辑。

  2. 声明 HomeActivity:在 mainModuleAndroidManifest.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 模块的清单中定义的 HomeActivityandroid:name 路径正确(需包含 mainModule 的包名)。

  • 解决资源冲突
    如果 mainModule 的资源(如布局、字符串)与 app 模块冲突,需在 mainModulebuild.gradle 中配置资源前缀:

    // mainModule/build.gradle
    android {
        resourcePrefix "main_"
    }
    

步骤 5:独立调试配置(可选)

若希望 mainModule 在开发阶段能独立运行(无需依赖 app 模块):

  1. mainModulebuild.gradle 中动态切换模块类型:
    // mainModule/build.gradle
    if (isDebug.toBoolean()) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    
  2. mainModulesrc/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):

  1. mainModule 中注册页面
    @Route(path = "/main/home")
    public class HomeActivity extends AppCompatActivity { ... }
    
  2. app 模块中初始化路由
    public class MyApp extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            ARouter.init(this);
        }
    }
    
  3. 通过路由路径跳转
    ARouter.getInstance().build("/main/home").navigation();
    

这种方式无需在 app 模块中直接依赖 mainModule,适合大型组件化工程。