编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

    • 库的解读

    • 专栏博客

      • 系统启动Zygote
      • Binder通信原理
      • Handler消息机制
      • Activity启动原理
      • 四大组件原理分析
      • AMS与组件管理
      • View绑制与渲染
      • 事件分发机制
      • Surface渲染原理
      • 自定义View设计
      • WMS窗口管理
      • PMS与APK安装
      • 虚拟机与类加载
      • 内存管理与GC
      • 线程与并发编程
      • 性能优化与监控
      • 序列化与数据存储
      • 组件化与路由设计
        • 一、引言:为什么需要组件化
        • 二、单体架构的困境
          • 2.1 单体架构的问题
          • 2.2 架构演进路线
        • 三、组件化架构设计
          • 3.1 分层架构
          • 3.2 组件通信的核心挑战
        • 四、Gradle模块化实现
          • 4.1 模块类型配置
          • 4.2 依赖管理
        • 五、组件间通信方案
          • 5.1 方案对比
          • 5.2 接口下沉方案
        • 六、路由框架的核心原理
          • 6.1 路由的本质
          • 6.2 路由框架的工作流程
        • 七、ARouter源码分析
          • 7.1 初始化过程
          • 7.2 导航过程
          • 7.3 拦截器机制
        • 八、APT注解处理器原理
          • 8.1 APT的工作机制
          • 8.2 RouteProcessor核心逻辑
        • 九、组件的独立调试与运行
          • 9.1 独立运行配置
          • 9.2 Mock与桩服务
        • 十、依赖注入与服务发现
          • 10.1 ServiceLoader机制
          • 10.2 ARouter的Provider机制
        • 十一、资源隔离与冲突解决
          • 11.1 资源命名冲突
          • 11.2 组件间资源共享
        • 十二、组件化实战架构设计
          • 12.1 完整的项目结构
        • 十三、面试高频问题与深度分析
          • 13.1 组件化和插件化的区别?
          • 13.2 ARouter是如何实现自动注册的?
          • 13.3 ARouter的路由是如何实现的?
        • 十四、组件化的常见问题与解决方案
          • 14.1 组件间通信问题
          • 14.2 资源冲突问题
          • 14.3 初始化顺序问题
        • 十五、总结
      • 插件化与热修复
      • NDK开发实践
      • WebView核心设计
      • ADB常见使用操作
    • 智能硬件

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

  • Apps
  • Android提升进阶
  • 专栏博客
杨充
2026-04-14
目录

组件化与路由设计

# 18.组件化与路由设计

# 目录介绍

  • 一、引言:为什么需要组件化
  • 二、单体架构的困境
    • 2.1 单体架构的问题
    • 2.2 架构演进路线
  • 三、组件化架构设计
    • 3.1 分层架构
    • 3.2 组件通信的核心挑战
  • 四、Gradle模块化实现
    • 4.1 模块类型配置
    • 4.2 依赖管理
  • 五、组件间通信方案
    • 5.1 方案对比
    • 5.2 接口下沉方案
  • 六、路由框架的核心原理
    • 6.1 路由的本质
    • 6.2 路由框架的工作流程
  • 七、ARouter源码分析
    • 7.1 初始化过程
    • 7.2 导航过程
    • 7.3 拦截器机制
  • 八、APT注解处理器原理
    • 8.1 APT的工作机制
    • 8.2 RouteProcessor核心逻辑
  • 九、组件的独立调试与运行
    • 9.1 独立运行配置
    • 9.2 Mock与桩服务
  • 十、依赖注入与服务发现
    • 10.1 ServiceLoader机制
    • 10.2 ARouter的Provider机制
  • 十一、资源隔离与冲突解决
    • 11.1 资源命名冲突
    • 11.2 组件间资源共享
  • 十二、组件化实战架构设计
    • 12.1 完整的项目结构
  • 十三、面试高频问题与深度分析
    • 13.1 组件化和插件化的区别?
    • 13.2 ARouter是如何实现自动注册的?
    • 13.3 ARouter的路由是如何实现的?
  • 十四、组件化的常见问题与解决方案
    • 14.1 组件间通信问题
    • 14.2 资源冲突问题
    • 14.3 初始化顺序问题
  • 十五、总结

# 一、引言:为什么需要组件化

当Android项目代码量超过20万行、开发人员超过10人时,单体架构会带来编译慢、耦合重、冲突多等问题。组件化通过将应用拆分为多个独立模块,解决大型项目的工程管理难题。

疑惑:组件化和模块化有什么区别?路由框架如何实现跨模块的页面跳转?


# 二、单体架构的困境

# 2.1 单体架构的问题

大型单体项目的痛点:

1. 编译速度
   单体项目全量编译 → 10分钟+
   组件化增量编译 → 30秒(只编译修改的模块)

2. 代码耦合
   A功能直接引用B功能的类
   → 修改B可能导致A编译失败
   → 无法独立测试单个功能

3. 协作冲突
   多人修改同一个module → git冲突频繁
   代码审查困难 → 无法确定修改的影响范围

4. 复用困难
   想复用登录模块到另一个App → 无法剥离
   强依赖关系 → 牵一发而动全身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.2 架构演进路线

单体架构 → 模块化 → 组件化 → 微应用

单体:所有代码在一个module中
模块化:按功能分module,但存在直接依赖
组件化:module间通过接口/路由通信,可独立运行
微应用:每个业务是独立App,通过IPC通信
1
2
3
4
5
6
架构演进的驱动力和关键特征:

1. 单体架构(10万行以下,5人以下团队)
   → 所有代码一个module
   → 构建快(代码量小时)
   → 问题:代码量增长后编译慢、冲突多、无法并行开发

2. 模块化(10-50万行,5-15人团队)
   → 按功能/业务拆分为多个module
   → 各module之间可以直接依赖
   → 问题:module间耦合严重,A依赖B、B依赖C
   → 修改底层module可能导致大量上层编译失败

3. 组件化(50万行以上,15人以上团队)
   → module间通过接口/路由通信,禁止直接依赖
   → 每个组件可以独立编译、运行、测试
   → 关键技术:路由框架 + 接口下沉 + APT
   → 问题:需要额外的基础设施(路由、服务发现)

4. 微应用(超大规模,如支付宝/美团)
   → 每个业务是独立App/插件
   → 通过IPC/ContentProvider通信
   → 动态加载/卸载业务
   → 问题:架构复杂度高,插件化方案受系统版本限制

选择原则:
  不要过度设计 → 项目规模决定架构选择
  单体够用就不要组件化 → 增加复杂度有成本
  组件化的核心价值 → 编译速度和团队并行开发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 三、组件化架构设计

# 3.1 分层架构

组件化分层模型:

┌─────────────────────────────────────────────┐
│  App壳工程(app module)                      │
│  只负责组装各业务组件,几乎无业务代码            │
├─────────────────────────────────────────────┤
│  业务组件层(Business Components)             │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐          │
│  │登录  │ │首页  │ │商品  │ │订单  │          │
│  │模块  │ │模块  │ │模块  │ │模块  │          │
│  └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘          │
│     │       │       │       │              │
│     └───────┴───┬───┴───────┘              │
├─────────────────┼───────────────────────────┤
│  公共服务层       │                           │
│  ┌─────────────┼─────────────┐              │
│  │路由服务  │网络服务  │图片服务  │              │
│  │组件通信  │API管理  │加载缓存  │              │
│  └─────────────┴─────────────┘              │
├─────────────────────────────────────────────┤
│  基础库层(Base Libraries)                    │
│  ├── 工具类(StringUtils, DateUtils等)        │
│  ├── UI组件(通用Dialog, Toast封装等)          │
│  ├── 网络框架(OkHttp/Retrofit封装)           │
│  └── 存储框架(SP/Room/MMKV封装)              │
└─────────────────────────────────────────────┘

关键规则:
1. 上层可以依赖下层,下层不能依赖上层
2. 同层组件之间不能直接依赖
3. 业务组件间通过路由/接口通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 3.2 组件通信的核心挑战

问题:业务组件A如何调用业务组件B的方法?

直接依赖(错误):
module_a → implementation project(':module_b')
→ 违反了组件独立性原则

正确方案:
1. 路由(页面跳转)
   ARouter.getInstance().build("/order/detail").withLong("id", 123).navigation()

2. 接口下沉(服务调用)
   → 接口定义在公共模块
   → 实现在业务模块
   → 通过ServiceLoader/SPI发现实现

3. 事件总线(松耦合通知)
   EventBus/LiveData 发送跨模块事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 四、Gradle模块化实现

# 4.1 模块类型配置

// 组件可以在Application和Library模式间切换
// gradle.properties
isLoginModuleRunAlone=false  // true时作为独立App运行

// module_login/build.gradle
if (isLoginModuleRunAlone.toBoolean()) {
    apply plugin: 'com.android.application'  // 独立运行
} else {
    apply plugin: 'com.android.library'       // 作为库被依赖
}

android {
    defaultConfig {
        if (isLoginModuleRunAlone.toBoolean()) {
            applicationId "com.example.login"  // 独立运行时需要applicationId
        }
    }
    
    sourceSets {
        main {
            if (isLoginModuleRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 4.2 依赖管理

// 统一版本管理(versions.gradle或Version Catalog)
// gradle/libs.versions.toml
[versions]
kotlin = "1.9.0"
retrofit = "2.9.0"
room = "2.6.0"

[libraries]
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }

// 使用
dependencies {
    implementation(libs.retrofit)
    implementation(libs.room.runtime)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 五、组件间通信方案

# 5.1 方案对比

┌──────────────┬────────────┬──────────────┬──────────┐
│ 方案          │ 适用场景    │ 优点          │ 缺点      │
├──────────────┼────────────┼──────────────┼──────────┤
│ 隐式Intent   │ 页面跳转    │ 系统原生      │ 不安全    │
│ 路由框架      │ 页面跳转    │ 编译检查、拦截 │ 需要框架  │
│ 接口下沉+SPI  │ 服务调用    │ 类型安全      │ 模板代码  │
│ EventBus     │ 事件通知    │ 解耦         │ 调试困难  │
│ BroadcastReceiver│广播     │ 系统级       │ 性能差    │
└──────────────┴────────────┴──────────────┴──────────┘
1
2
3
4
5
6
7
8
9

# 5.2 接口下沉方案

// 公共模块(module_common)中定义接口
public interface ILoginService {
    boolean isLogin();
    UserInfo getCurrentUser();
    void startLoginActivity(Context context);
}

// 登录模块(module_login)中实现
@Route(path = "/login/service")
public class LoginServiceImpl implements ILoginService {
    @Override
    public boolean isLogin() {
        return LoginManager.getInstance().isLogin();
    }
    
    @Override
    public UserInfo getCurrentUser() {
        return LoginManager.getInstance().getUser();
    }
}

// 其他模块中使用
ILoginService loginService = ARouter.getInstance()
    .navigation(ILoginService.class);
if (!loginService.isLogin()) {
    loginService.startLoginActivity(this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 六、路由框架的核心原理

# 6.1 路由的本质

路由框架解决的核心问题:
→ 如何在不直接依赖目标类的情况下,通过字符串路径跳转到目标页面

传统方式(有依赖):
Intent intent = new Intent(this, OrderDetailActivity.class);
// 必须能访问OrderDetailActivity类 → 必须依赖order模块

路由方式(无依赖):
ARouter.getInstance().build("/order/detail").navigation();
// 只需要知道路径字符串 → 不需要依赖order模块

路由框架的核心就是维护一个 路径→目标 的映射表
1
2
3
4
5
6
7
8
9
10
11
12

# 6.2 路由框架的工作流程

编译时(APT处理):
1. 扫描所有@Route注解
2. 生成路由表代码(路径 → Activity.class)

运行时(初始化):
3. 加载所有生成的路由表到内存HashMap

运行时(导航):
4. 根据路径查找目标Activity.class
5. 构建Intent并启动

详细流程:
┌──────────────────────────────────┐
│  编译时                           │
│  @Route("/order/detail")         │
│  class OrderDetailActivity       │
│       ↓ APT                      │
│  生成 ARouter$$Route$$order.java │
│  {"/order/detail" → OrderDetail} │
└──────────────────────────────────┘
         ↓ 打包到APK
┌──────────────────────────────────┐
│  运行时                           │
│  ARouter.init()                  │
│  → 扫描dex找到所有生成的路由类     │
│  → 加载到 HashMap<String, Class> │
│                                  │
│  ARouter.build("/order/detail")  │
│  → 从HashMap查找                 │
│  → 找到OrderDetailActivity.class │
│  → new Intent(ctx, clazz)       │
│  → startActivity(intent)        │
└──────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 七、ARouter源码分析

# 7.1 初始化过程

// ARouter.init() → LogisticsCenter.init()
public static void init(Application application) {
    // 1. 找到所有APT生成的路由类
    Set<String> routerMap;
    if (registerByPlugin) {
        // Gradle插件方式(推荐,更快)
        // 插件在编译时已经将所有路由类注册到loadRouterMap()方法中
    } else {
        // 扫描dex方式(兜底方案)
        routerMap = ClassUtils.getFileNameByPackageName(
            mContext, "com.alibaba.android.arouter.routes");
        // 扫描所有包名以arouter.routes开头的类
    }
    
    // 2. 反射创建实例并调用loadInto()加载路由表
    for (String className : routerMap) {
        Class<?> clazz = Class.forName(className);
        Object instance = clazz.getConstructor().newInstance();
        if (instance instanceof IRouteGroup) {
            ((IRouteGroup) instance).loadInto(Warehouse.groupsIndex);
        } else if (instance instanceof IProviderGroup) {
            ((IProviderGroup) instance).loadInto(Warehouse.providersIndex);
        } else if (instance instanceof IInterceptorGroup) {
            ((IInterceptorGroup) instance).loadInto(Warehouse.interceptorsIndex);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 7.2 导航过程

// ARouter.build(path).navigation()
// → _ARouter.getInstance().navigation(context, postcard, requestCode, callback)

protected Object navigation(Context context, Postcard postcard, int requestCode, ...) {
    // 1. 预处理:补全路由信息
    LogisticsCenter.completion(postcard);
    // 从Warehouse中查找路径对应的RouteMeta
    // 填充destination(目标Class)、type(Activity/Fragment/Provider)等
    
    // 2. 执行拦截器链
    interceptorService.doInterceptions(postcard, new InterceptorCallback() {
        @Override
        public void onContinue(Postcard postcard) {
            _navigation(postcard, requestCode, callback);
        }
        
        @Override
        public void onInterrupt(Throwable exception) {
            // 被拦截,不继续导航
        }
    });
}

private Object _navigation(Postcard postcard, int requestCode, ...) {
    switch (postcard.getType()) {
        case ACTIVITY:
            Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());
            
            // 在UI线程启动Activity
            ActivityCompat.startActivity(context, intent, postcard.getOptionsBundle());
            break;
            
        case PROVIDER:
            return postcard.getProvider();  // 返回服务实例
            
        case FRAGMENT:
            Class<?> fragmentClass = postcard.getDestination();
            Fragment fragment = fragmentClass.getConstructor().newInstance();
            fragment.setArguments(postcard.getExtras());
            return fragment;
    }
    return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 7.3 拦截器机制

// 拦截器:在导航前执行,可以中断导航
@Interceptor(priority = 8, name = "登录拦截器")
public class LoginInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        if (postcard.getExtra() == NEED_LOGIN && !isLogin()) {
            // 需要登录但未登录 → 跳转登录页
            ARouter.getInstance().build("/login/main").navigation();
            callback.onInterrupt(new RuntimeException("need login"));
        } else {
            callback.onContinue(postcard);  // 继续导航
        }
    }
}

// 拦截器链的执行是按priority排序的
// 优先级小的先执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 八、APT注解处理器原理

# 8.1 APT的工作机制

APT(Annotation Processing Tool)工作流程:

Java源码 → javac编译器 → APT处理 → 生成新的Java源码 → 继续编译

1. 编译器扫描源码中的注解
2. 调用对应的注解处理器(Processor)
3. 处理器分析注解信息,生成新的Java文件
4. 新生成的文件也参与编译

ARouter的APT处理器:
@Route("/order/detail")
class OrderDetailActivity extends Activity {}

        ↓ APT处理

// 自动生成
public class ARouter$$Group$$order implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/order/detail", 
            RouteMeta.build(RouteType.ACTIVITY, 
                OrderDetailActivity.class, "/order/detail", "order"));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 8.2 RouteProcessor核心逻辑

@AutoService(Processor.class)
@SupportedAnnotationTypes("com.alibaba.android.arouter.facade.annotation.Route")
public class RouteProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 获取所有被@Route标注的元素
        Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
        
        // 按group分组
        Map<String, Set<RouteMeta>> groupMap = new HashMap<>();
        for (Element element : routeElements) {
            Route route = element.getAnnotation(Route.class);
            String path = route.path();
            String group = extractGroup(path);  // 从路径提取分组:"/order/detail" → "order"
            
            RouteMeta routeMeta = new RouteMeta(route, element);
            groupMap.computeIfAbsent(group, k -> new TreeSet<>()).add(routeMeta);
        }
        
        // 为每个group生成路由表类
        for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
            generateRouteGroupFile(entry.getKey(), entry.getValue());
        }
        
        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 九、组件的独立调试与运行

# 9.1 独立运行配置

组件独立运行的配置:

module_login/
├── src/
│   ├── main/
│   │   ├── java/         ← 业务代码
│   │   ├── res/          ← 资源
│   │   └── AndroidManifest.xml  ← 库模式清单
│   └── debug/            ← 独立运行时额外资源
│       ├── java/
│       │   └── DebugLoginActivity.java  ← 调试入口
│       └── AndroidManifest.xml          ← 独立运行清单(有Application和启动Activity)
└── build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13

# 9.2 Mock与桩服务

// 组件独立运行时,依赖的其他模块服务不可用
// 需要Mock服务实现

// 在debug目录下提供Mock实现
public class MockUserService implements IUserService {
    @Override
    public UserInfo getCurrentUser() {
        return new UserInfo("test_user", "测试用户");
    }
}

// 在独立运行的Application中注册Mock服务
public class DebugApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ServiceRegistry.register(IUserService.class, new MockUserService());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 十、依赖注入与服务发现

# 10.1 ServiceLoader机制

// Java SPI(Service Provider Interface)在Android中的应用

// 1. 定义接口(module_common)
public interface IPayService {
    void pay(PayRequest request, PayCallback callback);
}

// 2. 实现接口(module_pay)
// META-INF/services/com.example.common.IPayService 文件内容:
// com.example.pay.WeChatPayService

public class WeChatPayService implements IPayService {
    @Override
    public void pay(PayRequest request, PayCallback callback) {
        // 微信支付逻辑
    }
}

// 3. 发现服务(任意模块)
ServiceLoader<IPayService> loader = ServiceLoader.load(IPayService.class);
for (IPayService service : loader) {
    service.pay(request, callback);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 10.2 ARouter的Provider机制

// ARouter的IProvider是更方便的服务发现方案

// 定义(module_common)
public interface IAccountService extends IProvider {
    boolean isVIP();
}

// 实现(module_account)
@Route(path = "/account/service")
public class AccountServiceImpl implements IAccountService {
    @Override
    public void init(Context context) {}
    
    @Override
    public boolean isVIP() {
        return AccountManager.isVIP();
    }
}

// 使用(任意模块)
// 方式1:路径发现
IAccountService service = (IAccountService) ARouter.getInstance()
    .build("/account/service").navigation();

// 方式2:类型发现(推荐)
@Autowired
IAccountService accountService;
// ARouter自动注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 十一、资源隔离与冲突解决

# 11.1 资源命名冲突

问题:不同模块可能有同名资源
module_a: res/layout/activity_main.xml
module_b: res/layout/activity_main.xml
→ 合并时会冲突,后编译的覆盖先编译的

解决方案:资源前缀
android {
    resourcePrefix "login_"  // 强制资源名以login_开头
}
// login_activity_main.xml
// login_ic_logo.png
// login_color_primary
1
2
3
4
5
6
7
8
9
10
11
12

# 11.2 组件间资源共享

公共资源管理策略:

base_res模块(资源库)
├── colors.xml      ← 统一颜色定义
├── dimens.xml      ← 统一尺寸定义
├── styles.xml      ← 统一主题样式
├── strings.xml     ← 通用字符串
└── drawable/       ← 通用图标

所有业务模块 → 依赖base_res
→ 保证视觉一致性
→ 避免资源重复
1
2
3
4
5
6
7
8
9
10
11
12

# 十二、组件化实战架构设计

# 12.1 完整的项目结构

项目结构示例:

app/                          ← 壳工程(组装各模块)
├── module_common/            ← 公共接口定义
├── module_base/              ← 基础库(网络、存储、UI组件)
├── module_router/            ← 路由配置
├── module_login/             ← 登录业务
├── module_home/              ← 首页业务
├── module_shop/              ← 商城业务
├── module_order/             ← 订单业务
├── module_mine/              ← 个人中心
└── module_share/             ← 分享组件

依赖关系:
app → [login, home, shop, order, mine]
login/home/shop/order/mine → [common, base, router]
common → [base]
base → [第三方库]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 十三、面试高频问题与深度分析

# 13.1 组件化和插件化的区别?

组件化:
→ 编译时决定模块组合
→ 所有模块在同一个APK中
→ 通过路由/接口通信
→ 目的:工程管理、解耦

插件化:
→ 运行时动态加载模块(单独的APK/dex)
→ 模块可以不在主APK中
→ 通过反射/Hook实现
→ 目的:动态更新、减少包体积
1
2
3
4
5
6
7
8
9
10
11

# 13.2 ARouter是如何实现自动注册的?

两种方式:

1. 运行时扫描dex(默认方式)
   → 遍历所有dex文件
   → 找到包名为arouter.routes的类
   → 反射实例化并加载路由表
   → 缺点:首次启动耗时

2. Gradle插件方式(arouter-register)
   → 编译时扫描所有class文件
   → 在LogisticsCenter.loadRouterMap()方法中插入代码
   → 运行时直接调用,无需扫描dex
   → 优点:初始化更快
1
2
3
4
5
6
7
8
9
10
11
12
13

# 13.3 ARouter的路由是如何实现的?

ARouter路由实现原理:

1. 编译时(APT)
   ├── 扫描所有@Route注解
   ├── 为每个模块生成路由表类
   │   ARouter$$Group$$xxx implements IRouteGroup {
   │       void loadInto(Map<String, RouteMeta> atlas) {
   │           atlas.put("/app/main", RouteMeta.build(
   │               RouteType.ACTIVITY, MainActivity.class, ...));
   │       }
   │   }
   └── 生成的类按模块组织

2. 运行时(初始化)
   ├── 扫描dex文件找到所有ARouter$$开头的类
   ├── 加载路由表到内存HashMap
   └── 按Group分组,延迟加载

3. 导航时
   ARouter.getInstance().build("/app/main").navigation()
   ├── 查找路由表获取目标Class
   ├── 依次执行拦截器链
   ├── 构建Intent
   └── startActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 十四、组件化的常见问题与解决方案

# 14.1 组件间通信问题

组件化后,模块之间不能直接引用,通信成为核心问题:

组件间通信的五种方案:

1. 路由通信(ARouter/TheRouter)
   └── 通过URL路径跳转页面
   └── 优点:解耦,支持拦截器
   └── 缺点:只支持页面跳转,不支持数据回调

2. 接口下沉(推荐)
   └── 将接口定义放在公共模块(base)中
   └── 各组件实现接口,通过SPI/路由注册
   └── 调用方通过接口获取实现
   └── 优点:类型安全,IDE支持好

3. EventBus事件总线
   └── 发布-订阅模式
   └── 优点:完全解耦
   └── 缺点:事件追踪困难,容易滥用

4. ContentProvider
   └── 系统级跨进程数据共享方案
   └── 优点:标准Android机制
   └── 缺点:API较重,适合数据密集场景

5. BroadcastReceiver
   └── 应用内广播(LocalBroadcast)
   └── 优点:系统支持
   └── 缺点:只能传递基本数据类型

推荐组合:路由(页面跳转)+ 接口下沉(服务调用)+ EventBus(事件通知)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 14.2 资源冲突问题

资源冲突的场景和解决方案:

问题:多个模块定义了同名资源
  module-a: res/layout/item_list.xml
  module-b: res/layout/item_list.xml
  合并后只保留一个 → 界面显示异常

解决方案1:资源前缀(推荐)
  android {
      resourcePrefix "modulea_"
  }
  // 资源必须以"modulea_"开头
  // 编译时检查,不符合命名规范则报错

解决方案2:资源包名隔离
  不同模块使用不同的包名
  资源ID的packageId部分不同

解决方案3:自定义Lint规则
  编写Lint规则检查资源命名规范
  CI/CD中强制执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 14.3 初始化顺序问题

组件化的初始化问题:

问题:各组件都有初始化逻辑,如何保证顺序?

方案1:Application中手动初始化
  缺点:Application需要知道所有组件,不够解耦

方案2:ContentProvider自动初始化
  利用ContentProvider在Application.onCreate之前创建的特性
  每个组件提供一个初始化ContentProvider
  缺点:启动时创建大量ContentProvider影响性能

方案3:App Startup(Jetpack推荐)
  统一的初始化框架
  支持声明依赖关系和初始化顺序
  合并为单个ContentProvider,减少开销
  
  class NetworkInitializer : Initializer<NetworkClient> {
      override fun create(context: Context): NetworkClient {
          return NetworkClient.init(context)
      }
      override fun dependencies(): List<Class<out Initializer<*>>> {
          return listOf(LoggerInitializer::class.java) // 依赖Logger先初始化
      }
  }

方案4:自定义初始化框架
  使用APT扫描各模块的初始化器
  在运行时按拓扑排序执行
  支持异步初始化和延迟初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 十五、总结

组件化知识图谱:

架构设计
├── 分层模型 → app壳/业务层/公共服务/基础库
├── 通信方案 → 路由/接口下沉/事件总线
└── Gradle管理 → 模块切换/依赖管理/资源隔离

路由原理
├── APT → 编译时生成路由表
├── 路由表 → HashMap<路径, 目标Class>
├── 拦截器 → 导航前置处理(登录校验等)
└── 服务发现 → IProvider接口注入

工程实践
├── 独立调试 → Application/Library切换
├── Mock服务 → 隔离测试各模块
├── 资源前缀 → 避免命名冲突
└── 版本管理 → 统一依赖版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
上次更新: 2026/06/10, 11:13:41
序列化与数据存储
插件化与热修复

← 序列化与数据存储 插件化与热修复→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式