Android 组件化开发实践

LiveApp 直播项目为示例,直接从代码出发介绍 Android 组件化的落地实现。

核心思路一句话:业务模块互不依赖、可独立运行,壳工程负责集成打包。下面全部围绕直播业务场景下的关键代码展开。

一、项目结构

bash 复制代码
LiveApp/
├── lib_common          # 公共库:网络、图片加载、路由、Base 类、直播基础 SDK 封装
├── module_app          # 空壳工程:集成所有业务组件,配置签名/混淆/多 Dex,输出 APK
├── module_live         # 业务组件:直播间、推流、拉流、房间列表
├── module_gift         # 业务组件:礼物面板、礼物特效、礼物商城
├── module_im           # 业务组件:弹幕、公屏聊天、私信
├── module_user         # 业务组件:登录、个人主页、关注/粉丝
├── build.gradle        # 根构建脚本
├── settings.gradle     # 子模块声明
├── versions.gradle     # 统一依赖版本
└── gradle.properties   # isModule 模式开关

依赖方向:module_app → 业务组件 → lib_common,业务组件之间零依赖


二、模式切换:isModule 开关

组件化的第一个关键点:业务模块在集成模式下是 library,开发调试时切换为 application 独立运行,整个切换由一个 Gradle 开关控制。

2.1 定义开关

ini 复制代码
# gradle.properties
# false = 集成模式,业务模块作为 library 被壳工程引用
# true  = 组件模式,业务模块作为 application 独立运行
isModule=false

2.2 业务模块按开关切换插件

arduino 复制代码
// module_live/build.gradle
if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'   // 组件模式:独立 App
} else {
    apply plugin: 'com.android.library'       // 集成模式:普通依赖库
}

2.3 组件模式下配置独立 applicationId

arduino 复制代码
// module_live/build.gradle
defaultConfig {
    if (isModule.toBoolean()) {
        applicationId "com.live.module_live"   // 按各模块实际包名配置
    }
}

2.4 壳工程只在集成模式下引入业务组件

scss 复制代码
// module_app/build.gradle
dependencies {
    implementation project(':lib_common')
    if (!isModule.toBoolean()) {
        implementation project(':module_live')
        implementation project(':module_gift')
        implementation project(':module_im')
        implementation project(':module_user')
    }
}

三、双份 AndroidManifest 适配

application 插件要求模块声明 LAUNCHER 入口,library 则不允许。解决方案:每个业务模块准备两份清单文件,用 sourceSets 按模式切换。

3.1 组件模式清单:src/main/module/AndroidManifest.xml

自带图标和应用名,声明 LAUNCHER 入口:

ini 复制代码
<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    <activity android:name=".LiveRoomActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

3.2 集成模式清单:src/main/AndroidManifest.xml

只注册页面,不带入口,图标/主题交给壳工程:

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <!-- 只注册页面,不含 LAUNCHER 入口 -->
        <activity android:name=".LiveRoomActivity" />
        <activity android:name=".RoomListActivity" />
    </application>
</manifest>

3.3 sourceSets 按模式加载,同时排除调试代码

arduino 复制代码
// module_live/build.gradle
sourceSets {
    main {
        if (isModule.toBoolean()) {
            manifest.srcFile 'src/main/module/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java {
                exclude 'debug/**'   // 集成模式下不编译模块内调试代码
            }
        }
    }
}

四、统一依赖管理:versions.gradle + lib_common

4.1 所有版本集中声明

ini 复制代码
// versions.gradle
ext.deps = [:]

def versions = [:]
versions.android_gradle_plugin = '3.5.2'
versions.kotlin = "1.1.51"
versions.support = "27.0.2"
versions.arouter_api = "1.3.1"
versions.arouter_compiler = "1.1.4"
versions.live_sdk = "x.y.z"          // 直播推拉流 SDK
versions.im_sdk = "x.y.z"            // IM / 弹幕 SDK
versions.room = "x.y.z"              // Room 数据库
// ...

4.2 lib_common 用 api 下发基础能力

less 复制代码
// lib_common/build.gradle
dependencies {
    api fileTree(include: ['*.jar'], dir: 'libs')
    api deps.support.app_compat
    api deps.retrofit.runtime              // 网络:Retrofit + RxJava
    api deps.glide                         // 图片:Glide
    api deps.arouter_api                   // 路由:ARouter
    api deps.live_sdk                      // 直播:推流/拉流 SDK
    api deps.im_sdk                        // IM:弹幕/聊天 SDK
    // ...
}

api 会向下游传递依赖,业务模块只需依赖 lib_common 一行,即可直接使用网络、图片、路由、直播、IM 等基础能力。


五、跨模块通信:ARouter

业务组件之间不能引用对方的类,页面跳转统一走 ARouter 路由。

5.1 壳工程 Application 初始化

scala 复制代码
public class LiveApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (BuildConfig.DEBUG) {
            ARouter.openDebug();
            ARouter.openLog();
        }
        ARouter.init(this);
    }
}

5.2 目标页面用 @Route 声明路径

scala 复制代码
// module_live
@Route(path = "/live/roomList")
public class RoomListActivity extends AppCompatActivity { }

@Route(path = "/live/room")
public class LiveRoomActivity extends AppCompatActivity { }

// module_gift
@Route(path = "/gift/panel")
public class GiftPanelActivity extends AppCompatActivity { }

@Route(path = "/gift/store")
public class GiftStoreActivity extends AppCompatActivity { }

// module_im
@Route(path = "/im/chat")
public class ChatActivity extends AppCompatActivity { }

// module_user
@Route(path = "/user/profile")
public class UserProfileActivity extends AppCompatActivity { }

5.3 调用方只认 path,不依赖目标模块

scss 复制代码
// module_live/LiveRoomActivity.java ------ 与 gift / im / user 模块零耦合
ARouter.getInstance().build("/gift/panel").navigation();
ARouter.getInstance().build("/im/chat")
       .withString("roomId", roomId)
       .navigation();
ARouter.getInstance().build("/user/profile")
       .withLong("userId", anchorId)
       .navigation();

5.4 各业务模块配置注解处理器

ini 复制代码
// 各业务模块 build.gradle
defaultConfig {
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [ moduleName : project.getName() ]
        }
    }
}

dependencies {
    annotationProcessor deps.arouter_compiler
}

六、日常开发流程

  1. 独立调试gradle.propertiesisModule=true → Sync → 直接运行 module_live / module_gift / module_im / module_user 等单个组件;
  2. 集成联调 :改回 isModule=false → Sync → 运行壳工程 module_app,验证完整直播流程。

七、注意事项

  • 路由 path 全局唯一 :按模块加前缀约定(/live//gift//im//user/);
  • 资源命名 :加模块前缀或配置 resourcePrefix,避免合并时资源冲突(如直播相关 layout 命名 live_xxx,礼物 gift_xxx);
  • 版本一致 :SDK 与三方库版本统一走 versions.gradle
  • 权限声明:直播推拉流、录音、摄像头权限在壳工程统一声明,也可按模块分清单文件由壳工程合并;
  • 生命周期 :直播后台保活、音视频生命周期跨模块时注意统一在壳工程 Application 初始化并暴露接口给业务模块调用;
  • 混淆规则:各模块各自维护,壳工程统一引用。

参考

  • 项目源码:LiveApp
  • 阿里 ARouter 路由框架
相关推荐
MayBaymax1 小时前
MongoDB 基础概念
java·数据库·mongodb
mmsx1 小时前
Android 几何构造器的双栈机:链式 API 底层是怎么装配几何的
android
mmsx1 小时前
Android 地图卡成 PPT 之后:双渲染管线与空间网格渐进加载怎么救
android·app
泡海椒1 小时前
jquick-pdf 文本元素实战:段落、行内文本、制表符用法详解
java·开发语言·pdf
光影少年1 小时前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
摇滚侠1 小时前
《Spring Boot 3:高级与架构设计》第 1 章 元编程与元信息 个人理解 1
java·spring boot·笔记·后端
吃饱了得干活1 小时前
Java 单点登录实战:一条主线看懂 Session 共享、CAS 与 OAuth2+JWT
java·后端
xiaoqiMikko1 小时前
Jetty 又出一条走私(CVE-2026-19203):官方叫升 9.4.64,而 9.4.64 在 Central 上是 404
java·安全
Web4Browser1 小时前
浏览器指纹逆向到底在分析什么:从 JavaScript 采集、Canvas 与 WebGL 到环境一致性校验
java·服务器·前端