Android 组件化架构从零搭建指南

0 阅读7分钟

效果图

  • 非正式包每个模块独立运行

image.png

image.png

  • 正式包每个模块合并到主分支

image.png 点击视频浏览是可以正常跳转的

image.png 这里跳转到发布适配详情页面

image.png

一、项目概述

本文介绍如何从零开始搭建一个 Android 组件化/模块化工程,实现业务模块独立开发调试整体打包发布的灵活切换。

核心思路

┌──────────────────────────────────────────────────────────┐
│                    app (壳工程)                          │
│   ┌─────────┬─────────┬─────────┬─────────┬─────────┐   │
│   │app-     │app-     │app-     │app-     │app-     │   │
│   │account  │publish  │video    │store    │setting  │   │
│   └────┬────┴────┬────┴────┬────┴────┬────┴────┬────┘   │
│        └─────────┴─────────┼─────────┴─────────┘        │
│                            │                            │
│                    ┌───────┴───────┐                    │
│                    │   app-base    │                    │
│                    │  (基础组件层)  │                    │
│                    └───────┬───────┘                    │
│        ┌───────┬───────┬───┴───┬───────┬───────┐       │
│        │lib-   │lib-   │lib-   │lib-   │lib-   │       │
│        │data   │logging│perms  │img    │util   │       │
│        └───────┴───────┴───────┴───────┴───────┘       │
│                    (功能库层)                           │
└──────────────────────────────────────────────────────────┘

关键特性:

  • 业务模块可独立运行调试,也可合并打包发布
  • 通过 isRelease 开关一键切换模式
  • 模块间通过反射中介解耦通信
  • 统一管理依赖版本,避免版本冲突

二、模块层级划分

2.1 模块分类

模块类型命名规范说明示例
壳工程app最终打包入口,聚合所有业务模块app
业务模块app-xxx可独立运行的业务模块app-account, app-video
基础组件app-base所有模块共用的基础类app-base
功能库lib-xxx纯功能库,不含业务lib-data, lib-util

2.2 依赖关系原则

1. 上层模块可以依赖下层模块
2. 同层模块之间禁止直接依赖
3. 业务模块之间通过 ModuleMediator 间接通信

三、搭建步骤

3.1 创建基础工程结构

Step 1: 创建根项目

# settings.gradle
rootProject.name = "modulefy_architecture"
include ':app'
include ':app-base'
include ':app-account'
include ':app-setting'
include ':app-publish'
include ':app-video'
include ':app-store'
include ':lib-data'
include ':lib-logging'
include ':lib-permissions'
include ':lib-imgloader'
include ':lib-util'

Step 2: 配置根 build.gradle

// build.gradle (Project)
plugins {
    id 'com.android.application' version '7.2.1' apply false
    id 'com.android.library' version '7.2.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
​
// 引入全局环境变量
apply from: 'env.gradle'
​
task clean(type: Delete) {
    delete rootProject.buildDir
}

3.2 全局环境变量配置 (env.gradle)

核心作用:

  • 定义 isRelease 开关控制打包模式
  • 统一管理所有依赖版本
// env.gradle
ext {
    // ★ 核心开关:true=合并打包,false=独立调试
    isRelease = true
    
    // SDK 版本统一配置
    buildVersion = [
        compileSdk: 32,
        minSdk    : 21,
        targetSdk : 30
    ]
    
    // AndroidX 官方库版本
    androidx = [
        ktx             : "androidx.core:core-ktx:1.7.0",
        appcompat       : "androidx.appcompat:appcompat:1.3.1",
        material        : "com.google.android.material:material:1.4.0",
        constraintlayout: "androidx.constraintlayout:constraintlayout:2.0.4",
        // ... 其他依赖
    ]
    
    // 第三方库版本
    depends = [
        okhttp     : "com.squareup.okhttp3:okhttp:3.14.9",
        retrofit2  : "com.squareup.retrofit2:retrofit:2.9.0",
        gson       : "com.google.code.gson:gson:2.8.7",
        glide      : "com.github.bumptech.glide:glide:4.12.0",
        arouter    : "com.alibaba:arouter-api:1.5.2",
        // ... 其他依赖
    ]
    
    // 测试库
    test = [
        junit        : "junit:junit:4.13.2",
        junit_android: "androidx.test.ext:junit:1.1.3",
        espresso     : "androidx.test.espresso:espresso-core:3.4.0"
    ]
}

使用方式:

// 在任意模块中引用
dependencies {
    implementation androidx.appcompat
    implementation depends.retrofit2
}

3.3 业务模块通用配置 (app.gradle)

核心设计:动态切换 application/library 类型

// app.gradle
apply {
    if (isRelease) {
        // 发布模式:作为 library 被主模块依赖
        plugins.apply('com.android.library')
    } else {
        // 调试模式:作为独立 application 运行
        plugins.apply('com.android.application')
    }
    plugins.apply('org.jetbrains.kotlin.android')
}
​
android {
    compileSdk buildVersion.compileSdk
    
    defaultConfig {
        minSdk buildVersion.minSdk
        targetSdk buildVersion.targetSdk
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    
    // ★ 关键:根据模式切换 Manifest 文件
    sourceSets {
        main {
            if (isRelease) {
                // 发布模式使用精简 Manifest
                manifest.srcFile 'src/main/AndroidManifest.xml'
                // 排除调试代码
                java.exclude '**/debug/**'
            } else {
                // 调试模式使用带启动入口的 Manifest
                manifest.srcFile 'src/main/AndroidManifestDebug.xml'
            }
        }
    }
    
    buildFeatures {
        viewBinding true
    }
    
    // ... 其他配置
}
​
dependencies {
    // 业务模块统一依赖基础层
    implementation project(':app-base')
    implementation project(':lib-data')
}

3.4 双 Manifest 配置策略

问题背景:

  • 独立调试时需要 LAUNCHER 入口
  • 合并打包时只能有一个启动入口

解决方案:为业务模块准备两份 Manifest

AndroidManifest.xml (发布模式)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.github.jeffery.account">
​
    <application>
        <!-- 无启动入口,仅注册组件 -->
        <activity
            android:name=".AccountActivity"
            android:exported="false" />
    </application>
</manifest>

AndroidManifestDebug.xml (调试模式)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.github.jeffery.account">
​
    <application android:theme="@style/MyTheme">
        <!-- 带启动入口,可独立运行 -->
        <activity
            android:name=".AccountActivity"
            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.5 业务模块配置示例

// app-account/build.gradle
apply from: '../app.gradle'  // 引入通用配置
​
android {
    // ★ 资源前缀,防止资源名冲突
    resourcePrefix 'account_'
    
    defaultConfig {
        if (!isRelease) {
            // 调试模式才需要 applicationId
            applicationId 'com.github.jeffery.account'
            versionCode 1
            versionName '1.0'
        }
    }
}
​
dependencies {
    // 模块特有依赖
}

资源前缀说明:

  • 每个模块设置独立前缀 (account_, video_, store_ 等)
  • 防止多模块合并时资源文件名冲突
  • IDE 会提示不符合前缀的资源命名

3.6 壳工程配置

// app/build.gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
​
android {
    compileSdk buildVersion.compileSdk
​
    defaultConfig {
        applicationId "com.github.jeffery"
        minSdk buildVersion.minSdk
        targetSdk buildVersion.targetSdk
        versionCode 1
        versionName "1.0"
    }
    // ... 其他配置
}
​
dependencies {
    // 始终依赖基础层
    implementation project(':app-base')
    
    // ★ 仅发布模式依赖业务模块
    if (isRelease) {
        implementation project(':app-account')
        implementation project(':app-publish')
        implementation project(':app-video')
        implementation project(':app-store')
        implementation project(':app-setting')
    }
}

四、模块间通信

4.1 问题分析

┌─────────────┐         ┌─────────────┐
│ app-account │ ──×──→  │ app-setting │
└─────────────┘         └─────────────┘
      ↑                       ↑
      │    同层模块不能       │
      │    直接相互依赖       │
      └───────────────────────┘

限制原因:

  • 同层模块相互依赖会造成循环依赖
  • 独立调试时,其他业务模块可能不存在

4.2 解决方案:ModuleMediator 中介模式

// app-base 模块中定义
object ModuleMediator {
    // 定义各模块 Activity 的全限定类名
    const val ACTIVITY_ACCOUNT_CLASS = "com.github.jeffery.account.AccountActivity"
    const val ACTIVITY_SETTING_CLASS = "com.github.jeffery.setting.SettingActivity"
    const val ACTIVITY_PUBLISH_CLASS = "com.github.jeffery.publish.PublishActivity"
    const val ACTIVITY_VIDEO_CLASS = "com.github.jeffery.video.VideoActivity"
    const val ACTIVITY_STORE_CLASS = "com.github.jeffery.store.StoreActivity"
​
    // 各模块 Application 初始化类
    private const val APP_ACCOUNT_CLASS = "com.github.jeffery.account.App"
    private const val APP_SETTING_CLASS = "com.github.jeffery.setting.App"
    // ...
​
    // 模块初始化接口
    interface AppInitial {
        fun init(app: Application)
    }
​
    // 反射初始化所有模块
    fun init(app: Application) {
        val appClasses = arrayOf(
            APP_ACCOUNT_CLASS, APP_SETTING_CLASS,
            APP_PUBLISH_CLASS, APP_VIDEO_CLASS, APP_STORE_CLASS
        )
        for (claName in appClasses) {
            try {
                val clz = Class.forName(claName)
                val obj = clz.newInstance() as AppInitial
                obj.init(app)
            } catch (e: ClassNotFoundException) {
                // 模块不存在时忽略(独立调试场景)
                e.printStackTrace()
            }
        }
    }
}

4.3 BaseActivity 封装跨模块跳转

open class BaseActivity : AppCompatActivity() {
​
    // 通过类名字符串启动 Activity(反射方式)
    protected fun startActivity(className: String, bundle: Bundle? = null) {
        try {
            val clazz = Class.forName(className)
            val intent = Intent(this, clazz)
            bundle?.let { intent.putExtras(it) }
            startActivity(intent)
        } catch (e: ClassNotFoundException) {
            // 目标模块不存在时友好提示
            Toast.makeText(this, "模块未安装", Toast.LENGTH_SHORT).show()
        }
    }
}

4.4 使用示例

class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        
        // 跳转到账户模块
        binding.btnAccount.setOnClickListener {
            startActivity(ModuleMediator.ACTIVITY_ACCOUNT_CLASS)
        }
        
        // 跳转到设置模块
        binding.btnSetting.setOnClickListener {
            startActivity(ModuleMediator.ACTIVITY_SETTING_CLASS)
        }
    }
}

五、基础层设计 (app-base)

5.1 职责定义

app-base
├── BaseActivity.kt      # Activity 基类
├── BaseFragment.kt      # Fragment 基类
├── BaseViewModel.kt     # ViewModel 基类
├── BaseApplication.kt   # Application 基类
└── ModuleMediator.kt    # 模块通信中介

5.2 依赖配置

// app-base/build.gradle
plugins {
    id 'com.android.library'  // 始终是 library
    id 'org.jetbrains.kotlin.android'
}
​
dependencies {
    // 使用 api 传递依赖给上层模块
    api androidx.ktx
    api androidx.appcompat
    api androidx.material
    
    // 测试依赖
    testApi test.junit
    androidTestApi test.junit_android
    androidTestApi test.espresso
}

api vs implementation:

  • api:依赖会传递给依赖本模块的模块
  • implementation:依赖不传递,仅本模块可用

六、功能库层设计 (lib-xxx)

6.1 lib-data 示例(网络/数据层)

// lib-data/build.gradle
dependencies {
    // 网络请求
    api depends.okhttp
    api depends.okhttp_logging_interceptor
    api depends.retrofit2
    api depends.retrofit2_converter_gson
    api depends.retrofit2_adapter_rxjava
    
    // 数据处理
    api depends.gson
    api depends.rxjava
    api depends.rxandroid
}

6.2 功能库设计原则

库名职责对外暴露
lib-data网络请求、数据存储Retrofit Service、Repository
lib-logging日志打印Logger 工具类
lib-permissions权限申请权限检查/申请方法
lib-imgloader图片加载加载图片的扩展函数
lib-util通用工具各类工具方法

七、模式切换与调试

7.1 切换打包模式

// env.gradle
ext {
    isRelease = true   // true: 合并打包 | false: 独立调试
}

7.2 独立调试业务模块

  1. 设置 isRelease = false
  2. Sync Gradle
  3. 在 Run Configurations 中选择要调试的模块(如 app-account
  4. 直接运行该模块

7.3 合并打包发布

  1. 设置 isRelease = true
  2. Sync Gradle
  3. 运行 app 模块即可打包完整应用

八、进阶优化建议

8.1 使用 ARouter 替代反射

// 添加依赖
implementation depends.arouter
kapt depends.arouter_compiler
// 定义路由
@Route(path = "/account/main")
class AccountActivity : BaseActivity()
​
// 跳转
ARouter.getInstance().build("/account/main").navigation()

优势:

  • 编译期检查路由是否存在
  • 支持参数自动注入
  • 支持拦截器

8.2 接口下沉

将模块对外暴露的服务接口定义在 app-base,实现在各业务模块:

// app-base 定义接口
interface IAccountService {
    fun getUserInfo(): UserInfo?
    fun isLoggedIn(): Boolean
}
​
// app-account 实现
class AccountServiceImpl : IAccountService {
    override fun getUserInfo() = ...
    override fun isLoggedIn() = ...
}

8.3 组件化配置自动化

可使用 Gradle 插件自动配置,减少重复代码。


九、总结

核心要点回顾

  1. 模块分层:壳工程 → 业务模块 → 基础组件 → 功能库
  2. isRelease 开关:一键切换独立调试/合并打包
  3. 双 Manifest:解决启动入口冲突问题
  4. ModuleMediator:反射实现模块间解耦通信
  5. 资源前缀:防止多模块资源名冲突
  6. env.gradle:统一管理依赖版本

项目结构速览

android-multi-module-demo/
├── app/                    # 壳工程
├── app-base/              # 基础组件层
├── app-account/           # 业务模块:账户
├── app-setting/           # 业务模块:设置
├── app-publish/           # 业务模块:发布
├── app-video/             # 业务模块:视频
├── app-store/             # 业务模块:商店
├── lib-data/              # 功能库:网络/数据
├── lib-logging/           # 功能库:日志
├── lib-permissions/       # 功能库:权限
├── lib-imgloader/         # 功能库:图片加载
├── lib-util/              # 功能库:工具类
├── build.gradle           # 根构建脚本
├── env.gradle             # 全局环境变量
├── app.gradle             # 业务模块通用配置
└── settings.gradle        # 模块注册

通过以上架构,可以实现:

  • 多人并行开发不同业务模块
  • 单模块独立编译运行,加快开发效率
  • 一键合并打包,统一发布