编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • 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提升进阶

    • 库的解读

      • README
      • LeakCanary内存收集
      • Glide图片加载设计
      • OkHttp网络框架设计
      • EventBus事件总设计
      • ARouter路由实践设计
      • 专栏博客

      • 智能硬件

    • iOS开发和进阶

    • Web开发和进阶

    • Linux应用开发

    • Apps
    • Android提升进阶
    • 库的解读
    杨充
    2025-02-19
    目录

    ARouter路由实践设计

    # 05.ARouter路由框架设计原理

    # 目录介绍

    • 01.整体概述介绍
      • 1.1 项目背景介绍
      • 1.2 基础概念
      • 1.3 设计目标
      • 1.4 一些问题思考
    • 02.ARouter设计思路
      • 2.1 整体设计思路
      • 2.2 路由表生成设计
      • 2.3 路由表加载设计
      • 2.4 导航流程设计
      • 2.5 拦截器链设计
      • 2.6 服务发现设计
      • 2.7 参数自动注入设计
    • 03.APT编译时处理原理
      • 3.1 APT是什么
      • 3.2 注解处理器工作原理
      • 3.3 RouteProcessor生成代码
      • 3.4 InterceptorProcessor生成代码
      • 3.5 AutowiredProcessor生成代码
    • 04.ARouter初始化原理
      • 4.1 init()初始化流程
      • 4.2 路由表扫描机制
      • 4.3 分组加载策略
      • 4.4 Gradle Plugin优化加载
    • 05.导航过程源码分析
      • 5.1 build()构建Postcard
      • 5.2 navigation()执行导航
      • 5.3 路由查找过程
      • 5.4 拦截器执行过程
      • 5.5 最终跳转过程
    • 06.核心技术点思考
      • 6.1 为什么需要路由框架
      • 6.2 路由与原生跳转的对比
      • 6.3 分组懒加载的设计
      • 6.4 降级策略设计
      • 6.5 多模块路由协作
    • 07.ARouter与其他路由对比
      • 7.1 主流路由框架对比
      • 7.2 TheRouter的改进
      • 7.3 路由框架选型建议
    • 08.优秀代码设计深度解析
      • 8.1 设计模式应用全景
      • 8.2 Postcard继承体系设计
      • 8.3 Warehouse单例仓库核心设计
      • 8.4 LogisticsCenter路由补全算法
      • 8.5 拦截器CancelableCountDownLatch设计
      • 8.6 JavaPoet代码生成设计
      • 8.7 两级索引架构思想
      • 8.8 _ARouter门面模式设计
      • 8.9 Provider单例缓存设计
      • 8.10 设计思想总结
    • 09.面试高频问题深度解析
      • 9.1 经典面试题
      • 9.2 进阶面试题
      • 9.3 学习建议

    # 01.整体概述介绍

    # 1.1 项目背景介绍

    在组件化/模块化架构中,各模块之间不能直接引用对方的类,传统的Intent显式跳转方式无法跨模块使用。例如:

    传统跳转方式的问题:
    
    // 模块A想跳转到模块B的页面
    Intent intent = new Intent(context, ModuleBActivity.class);
    startActivity(intent);
    // 编译错误!模块A无法引用模块B的类
    
    组件化架构中的模块依赖关系:
    app
    ├── module-home(首页模块)
    ├── module-shop(商城模块)
    ├── module-user(用户模块)
    └── module-base(基础公共模块)
    
    module-home和module-shop之间不互相依赖
    → 无法直接使用对方的Activity类
    → 需要路由框架来解决跨模块跳转问题
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    ARouter是阿里巴巴开源的Android路由框架,它通过URL路径映射Activity/Fragment/Service等组件,解决了组件化架构中的页面跳转和服务调用问题。

    # 1.2 基础概念

    路由框架核心概念:
    
    路由(Route):
      URL路径到目标组件的映射关系
      例如:"/user/login" → LoginActivity.class
    
    路由表(Route Table):
      所有路由映射关系的集合
      在编译时由APT生成,运行时加载
    
    导航(Navigation):
      根据URL路径查找目标组件并执行跳转
    
    Postcard(明信片):
      封装一次导航的所有信息(路径、参数、动画等)
      类似于Intent的角色
    
    拦截器(Interceptor):
      在导航过程中可以拦截、修改或取消跳转
      如登录拦截器:未登录时跳转到登录页
    
    服务发现(Service Discovery):
      跨模块调用服务的机制
      通过接口调用而非直接引用实现类
    
    参数注入(Autowired):
      自动将Intent中的参数注入到目标页面的字段中
      类似于Spring的@Autowired
    
    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

    # 1.3 设计目标

    ARouter的核心设计目标:

    1. 解耦页面跳转:通过URL映射替代显式Intent,模块间无直接依赖
    2. 支持拦截器:在跳转前执行自定义逻辑(登录验证、权限检查等)
    3. 服务发现:跨模块调用接口服务,不依赖具体实现
    4. 参数自动注入:减少Intent参数解析的样板代码
    5. 降级策略:路由找不到目标时的兜底处理
    6. 编译时处理:APT编译时生成路由表,避免运行时反射开销

    # 1.4 一些问题思考

    思考问题:
    
    1. ARouter是如何实现编译时生成路由表的?APT原理是什么?
    2. 路由表是何时加载的?如何做到分组懒加载?
    3. 导航过程中路由是如何查找目标的?
    4. 拦截器是如何设计的?多个拦截器如何按顺序执行?
    5. 跨模块服务调用是如何实现的?接口下沉到哪里?
    6. ARouter在多模块项目中是如何协作的?
    7. ARouter有什么局限性?现在有哪些更好的替代方案?
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 02.ARouter设计思路

    # 2.1 整体设计思路

    ARouter的整体架构分为三层:

    ARouter整体架构:
    
    ┌──────────────────────────────────────┐
    │           API层(ARouter对外接口)     │
    │  init() / build() / navigation()     │
    │  inject() / addInterceptor()         │
    └────────────────┬─────────────────────┘
                     │
    ┌────────────────▼─────────────────────┐
    │           核心层(_ARouter内部实现)    │
    │  ┌─────────────────────────────────┐ │
    │  │ Warehouse(路由仓库)            │ │
    │  │ ├── groupsIndex (Group索引表)   │ │
    │  │ ├── routes (路由表)             │ │
    │  │ ├── providers (服务表)          │ │
    │  │ └── interceptors (拦截器表)      │ │
    │  └─────────────────────────────────┘ │
    │  ┌─────────────────────────────────┐ │
    │  │ LogisticsCenter(物流中心)      │ │
    │  │ 负责路由查找、Postcard填充       │ │
    │  └─────────────────────────────────┘ │
    │  ┌─────────────────────────────────┐ │
    │  │ InterceptorService(拦截器服务) │ │
    │  │ 责任链模式执行拦截器             │ │
    │  └─────────────────────────────────┘ │
    └────────────────┬─────────────────────┘
                     │
    ┌────────────────▼─────────────────────┐
    │         编译时(APT生成代码)          │
    │  RouteProcessor → 生成路由注册代码   │
    │  InterceptorProcessor → 拦截器注册   │
    │  AutowiredProcessor → 参数注入代码   │
    └──────────────────────────────────────┘
    
    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

    # 2.2 路由表生成设计

    ARouter使用APT(Annotation Processing Tool)在编译时扫描@Route注解,为每个模块生成路由注册代码:

    路由表生成过程:
    
    编译时:
    1. 开发者在Activity上添加@Route注解
       @Route(path = "/user/login")
       public class LoginActivity extends Activity { ... }
    
    2. APT扫描所有@Route注解
       RouteProcessor.process()
         → 扫描所有标记了@Route的类
         → 按group分组(path的第一段,如/user/login的group是"user")
         → 为每个group生成一个路由注册类
    
    3. 生成的代码示例:
       // 每个group对应一个类
       public class ARouter$$Group$$user implements IRouteGroup {
           @Override
           public void loadInto(Map<String, RouteMeta> atlas) {
               atlas.put("/user/login", RouteMeta.build(
                   RouteType.ACTIVITY,
                   LoginActivity.class,
                   "/user/login",
                   "user",
                   null,  // paramsType
                   -1,    // priority
                   -2147483648  // extra
               ));
           }
       }
    
       // 每个模块对应一个Root类(Group索引)
       public class ARouter$$Root$$moduleuser implements IRouteRoot {
           @Override
           public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
               routes.put("user", ARouter$$Group$$user.class);
           }
       }
    
    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

    # 2.3 路由表加载设计

    路由表加载策略 — 分组懒加载:
    
    初始化时(init):
      只加载Group索引(IRouteRoot)
      → 将每个group名称映射到对应的IRouteGroup类
      → 此时不加载具体路由信息
      → 启动速度快
    
    导航时(navigation):
      根据path提取group名称
      → 检查该group是否已加载
      ├── 已加载 → 直接从routes表中查找
      └── 未加载 → 实例化IRouteGroup → 调用loadInto()加载该group的所有路由
      → 后续同group的路由直接从内存查找
    
    示例:
    init()时加载:
      groupsIndex = {
          "user" → ARouter$$Group$$user.class,
          "shop" → ARouter$$Group$$shop.class,
          "home" → ARouter$$Group$$home.class
      }
    
    首次导航"/user/login"时:
      → group = "user"
      → 发现routes中没有 → 加载ARouter$$Group$$user
      → routes中加入 "/user/login" → LoginActivity
      → routes中加入 "/user/register" → RegisterActivity
      → 后续/user/*路由直接查找routes
    
    优势:
      → 初始化只加载索引,非常快
      → 路由信息按需加载,减少内存占用
      → 大型项目中路由可能有数百个,全量加载太慢
    
    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

    # 2.4 导航流程设计

    完整导航流程:
    
    ARouter.getInstance()
        .build("/user/login")          // 构建Postcard
        .withString("name", "yang")    // 附带参数
        .withInt("age", 25)
        .navigation(context)           // 执行导航
    
    Step 1: build("/user/login")
      → 创建Postcard对象
      → 提取path和group
      → Postcard继承自RouteMeta,附加了参数Bundle等
    
    Step 2: navigation(context)
      → _ARouter.getInstance().navigation(context, postcard, ...)
    
    Step 3: LogisticsCenter.completion(postcard)
      → 根据path查找RouteMeta
      ├── 找到 → 将RouteMeta信息填充到Postcard
      │    → 设置destination(目标Activity类)
      │    → 设置type(ACTIVITY/FRAGMENT/PROVIDER等)
      └── 找不到 → 触发降级策略
    
    Step 4: 执行拦截器链
      → 遍历所有注册的拦截器
      → 按priority依次执行
      → 任何拦截器可以中断导航
    
    Step 5: 执行跳转
      → 根据type执行不同逻辑:
      ├── ACTIVITY → 构建Intent + startActivity
      ├── FRAGMENT → 实例化Fragment并返回
      ├── PROVIDER → 获取/创建Provider实例
      └── 其他 → 对应处理
    
    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

    # 2.5 拦截器链设计

    拦截器机制设计:
    
    注册拦截器:
    @Interceptor(priority = 1, name = "登录拦截器")
    public class LoginInterceptor implements IInterceptor {
        @Override
        public void init(Context context) {
            // 拦截器初始化
        }
        
        @Override
        public void process(Postcard postcard, InterceptorCallback callback) {
            if (needLogin(postcard) && !isLoggedIn()) {
                // 拦截:跳转到登录页
                callback.onInterrupt(new RuntimeException("需要登录"));
            } else {
                // 放行:继续执行下一个拦截器
                callback.onContinue(postcard);
            }
        }
    }
    
    拦截器执行流程(责任链模式):
    navigation() → InterceptorServiceImpl.doInterceptions()
      → 在子线程中依次执行拦截器:
         index = 0
         while (index < interceptors.size()) {
             interceptor = interceptors.get(index)
             interceptor.process(postcard, new InterceptorCallback() {
                 onContinue: index++ → 执行下一个
                 onInterrupt: 中断导航,回调onLost
             })
         }
      → 所有拦截器都放行 → 执行最终跳转
      → 有超时机制(默认300秒),超时自动中断
    
    注意事项:
    ├── 拦截器在子线程中异步执行
    ├── 必须调用callback.onContinue()或callback.onInterrupt()
    ├── 不调用callback会导致导航卡住(直到超时)
    ├── priority数字越小优先级越高
    └── Fragment跳转不经过拦截器(isGreenChannel=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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42

    # 2.6 服务发现设计

    跨模块服务调用设计:
    
    1. 在公共模块(base)定义接口
       public interface IUserService extends IProvider {
           UserInfo getUserInfo();
           boolean isLoggedIn();
       }
    
    2. 在用户模块实现接口
       @Route(path = "/user/service")
       public class UserServiceImpl implements IUserService {
           @Override
           public void init(Context context) { }
           
           @Override
           public UserInfo getUserInfo() { return userInfo; }
           
           @Override
           public boolean isLoggedIn() { return token != null; }
       }
    
    3. 在其他模块通过ARouter获取服务
       // 方式1:通过路径发现
       IUserService userService = (IUserService) ARouter.getInstance()
           .build("/user/service")
           .navigation();
       
       // 方式2:通过类型发现(推荐)
       IUserService userService = ARouter.getInstance()
           .navigation(IUserService.class);
       
       boolean loggedIn = userService.isLoggedIn();
    
    服务发现原理:
      → Provider类型的路由在初始化时会被预加载
      → 通过路径查找:从routes表中查找
      → 通过类型查找:从providers表中查找(Key是接口Class)
      → Provider实例默认是单例模式(缓存在Warehouse中)
    
    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

    # 2.7 参数自动注入设计

    参数自动注入(@Autowired):
    
    使用方式:
    @Route(path = "/user/detail")
    public class UserDetailActivity extends Activity {
        @Autowired
        String name;
        
        @Autowired(name = "userId")  // 自定义参数名
        int id;
        
        @Autowired
        IUserService userService;  // 也可以注入Provider服务
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ARouter.getInstance().inject(this);  // 触发注入
        }
    }
    
    APT生成的注入代码:
    public class UserDetailActivity$$ARouter$$Autowired implements ISyringe {
        @Override
        public void inject(Object target) {
            UserDetailActivity substitute = (UserDetailActivity) target;
            substitute.name = substitute.getIntent().getStringExtra("name");
            substitute.id = substitute.getIntent().getIntExtra("userId", 0);
            substitute.userService = (IUserService) ARouter.getInstance()
                .navigation(IUserService.class);
        }
    }
    
    inject()调用流程:
    ARouter.getInstance().inject(this)
      → 查找 "类名$$ARouter$$Autowired" 类
      → 实例化并调用 inject(this)
      → 从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
    34
    35
    36
    37
    38

    # 03.APT编译时处理原理

    # 3.1 APT是什么

    APT(Annotation Processing Tool)是Java的注解处理工具,它在编译时扫描和处理注解,可以生成新的Java源文件。

    APT工作时机:
    
    .java源文件
      ↓ javac编译
    APT处理(在编译期间)
      → 扫描注解
      → 生成新的.java文件
      ↓ 继续编译
    .class文件
    
    关键:APT生成的代码在编译时完成,不影响运行时性能
    与运行时反射扫描相比,性能优势巨大
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 3.2 注解处理器工作原理

    ARouter的三个注解处理器:
    
    1. RouteProcessor
       处理@Route注解
       → 为每个module生成路由注册类
       → ARouter$$Root$$moduleName
       → ARouter$$Group$$groupName
    
    2. InterceptorProcessor
       处理@Interceptor注解
       → 生成拦截器注册类
       → ARouter$$Interceptors$$moduleName
    
    3. AutowiredProcessor
       处理@Autowired注解
       → 为每个使用@Autowired的类生成参数注入类
       → ClassName$$ARouter$$Autowired
    
    注解处理器继承AbstractProcessor:
    @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> elements = roundEnv.getElementsAnnotatedWith(Route.class);
            // 按group分组
            // 使用JavaPoet生成Java代码
            // 写入文件
        }
    }
    
    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

    # 3.3 RouteProcessor生成代码

    RouteProcessor的处理逻辑:
    
    1. 收集所有@Route注解的类
       → Activity, Fragment, Provider等
    
    2. 按group分组
       @Route(path = "/user/login") → group = "user"
       @Route(path = "/user/register") → group = "user"
       @Route(path = "/shop/detail") → group = "shop"
    
    3. 为每个group生成一个IRouteGroup实现类
       ARouter$$Group$$user:
         loadInto(atlas) {
             atlas.put("/user/login", RouteMeta.build(ACTIVITY, LoginActivity.class, ...));
             atlas.put("/user/register", RouteMeta.build(ACTIVITY, RegisterActivity.class, ...));
         }
    
    4. 为每个module生成一个IRouteRoot实现类
       ARouter$$Root$$moduleuser:
         loadInto(routes) {
             routes.put("user", ARouter$$Group$$user.class);
         }
    
    5. 使用JavaPoet库生成代码
       JavaPoet提供类型安全的代码生成API
       → TypeSpec.classBuilder() 生成类
       → MethodSpec.methodBuilder() 生成方法
       → CodeBlock.builder() 生成代码块
    
    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

    # 3.4 InterceptorProcessor生成代码

    InterceptorProcessor生成:
    
    输入:
    @Interceptor(priority = 1, name = "登录拦截")
    class LoginInterceptor implements IInterceptor { ... }
    
    @Interceptor(priority = 2, name = "权限检查")
    class PermissionInterceptor implements IInterceptor { ... }
    
    生成:
    public class ARouter$$Interceptors$$app implements IInterceptorGroup {
        @Override
        public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
            interceptors.put(1, LoginInterceptor.class);
            interceptors.put(2, PermissionInterceptor.class);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    # 3.5 AutowiredProcessor生成代码

    AutowiredProcessor生成:
    
    输入:
    @Route(path = "/user/detail")
    class UserDetailActivity extends Activity {
        @Autowired String name;
        @Autowired int age;
        @Autowired(name = "data") Parcelable userData;
    }
    
    生成:
    public class UserDetailActivity$$ARouter$$Autowired implements ISyringe {
        @Override
        public void inject(Object target) {
            UserDetailActivity t = (UserDetailActivity) target;
            t.name = t.getIntent().getStringExtra("name");
            t.age = t.getIntent().getIntExtra("age", 0);
            t.userData = t.getIntent().getParcelableExtra("data");
        }
    }
    
    支持的参数类型:
    ├── 基本类型:int, long, float, double, boolean, String
    ├── Serializable对象
    ├── Parcelable对象
    ├── IProvider服务(通过ARouter.navigation获取)
    └── Object类型(序列化为JSON,需自定义SerializationService)
    
    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

    # 04.ARouter初始化原理

    # 4.1 init()初始化流程

    ARouter.init(application) 初始化流程:
    
    ARouter.init(app)
      → _ARouter.init(app)
        → LogisticsCenter.init(context, executor)
          ↓
    Step 1: 获取APT生成类的包名
      packageName = "com.alibaba.android.arouter.routes"
    
    Step 2: 扫描该包名下所有类
      方式1: 扫描dex文件(默认方式)
        → 遍历所有dex文件
        → 找到以"com.alibaba.android.arouter.routes"开头的类
        → 耗时操作!
      
      方式2: 使用arouter-register Gradle Plugin(推荐)
        → 编译时通过Transform API扫描
        → 直接将注册代码插入到init()方法中
        → 运行时无需扫描,初始化速度大幅提升
    
    Step 3: 分类加载
      → 以"ARouter$$Root"开头的 → 加载到Warehouse.groupsIndex
      → 以"ARouter$$Interceptors"开头的 → 加载到Warehouse.interceptorsIndex
      → 以"ARouter$$Providers"开头的 → 加载到Warehouse.providersIndex
    
    Step 4: 初始化拦截器
      → 遍历interceptorsIndex
      → 实例化所有拦截器
      → 调用interceptor.init(context)
      → 存入Warehouse.interceptors
    
    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

    # 4.2 路由表扫描机制

    默认扫描机制(性能较差):
    
    ClassUtils.getFileNameByPackageName()
      → 获取应用的所有dex文件路径
      → 遍历每个dex文件
      → 使用DexFile.entries()获取所有类名
      → 过滤出以"com.alibaba.android.arouter.routes"开头的类
      → 返回类名集合
    
    性能问题:
      → 大型应用可能有数万个类
      → 遍历所有类名非常耗时(首次可能需要1-3秒)
      → 特别是multidex场景下,有多个dex文件需要扫描
      → 严重影响启动速度
    
    优化策略:
      → ARouter将扫描结果缓存到SharedPreferences
      → 下次启动直接读取缓存(版本号不变时)
      → 但首次安装或版本更新时仍需扫描
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    # 4.3 分组加载策略

    分组懒加载详细设计:
    
    Warehouse中的数据结构:
      groupsIndex: Map<String, Class<? extends IRouteGroup>>
        → 初始化时加载,存储group名称到Group类的映射
        → 如:{"user" → ARouter$$Group$$user.class}
      
      routes: Map<String, RouteMeta>
        → 按需加载,存储path到RouteMeta的映射
        → 如:{"/user/login" → RouteMeta(ACTIVITY, LoginActivity.class)}
    
    加载时机:
      init()时 → 只加载groupsIndex
      navigation("/user/login")时
        → group = "user"
        → 检查routes中是否有"/user/login"
        ├── 有 → 直接使用
        └── 没有 → 从groupsIndex取出ARouter$$Group$$user.class
           → 实例化 → 调用loadInto(routes)
           → 该group的所有路由加入routes
           → 从groupsIndex移除该group(已加载)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    # 4.4 Gradle Plugin优化加载

    arouter-register Gradle Plugin 优化原理:
    
    传统方式(运行时扫描dex):
    init() → 扫描dex → 找到ARouter$$Root$$xxx → 实例化 → 注册
    └── 首次启动耗时1-3秒
    
    Plugin方式(编译时注入):
    编译阶段:
      Gradle Plugin通过Transform API
      → 扫描所有class文件
      → 找到ARouter$$Root$$xxx等类
      → 将注册代码直接插入到LogisticsCenter.loadRouterMap()方法中
    
    生成的代码:
    void loadRouterMap() {
        registerByPlugin = true;
        register("com.alibaba.android.arouter.routes.ARouter$$Root$$moduleuser");
        register("com.alibaba.android.arouter.routes.ARouter$$Root$$moduleshop");
        register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$app");
        register("com.alibaba.android.arouter.routes.ARouter$$Providers$$app");
    }
    
    运行时:
    init() → 直接调用loadRouterMap() → 注册代码已在其中 → 无需扫描dex
    └── 初始化耗时从秒级降到毫秒级
    
    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

    # 05.导航过程源码分析

    # 5.1 build()构建Postcard

    ARouter.getInstance().build("/user/login")
    
    build(path):
      → 提取group(path的第一段)
      → 创建Postcard对象
      → Postcard继承RouteMeta
      → 额外包含:Bundle, flags, enterAnim, exitAnim等
    
    Postcard支持链式配置:
      .withString("key", "value")     // 字符串参数
      .withInt("age", 25)             // 整数参数
      .withParcelable("data", obj)    // Parcelable参数
      .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK)  // Intent flags
      .withTransition(R.anim.enter, R.anim.exit) // 转场动画
      .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)  // 添加flags
      .withObject("complex", obj)     // 复杂对象(需SerializationService)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    # 5.2 navigation()执行导航

    navigation()核心流程:
    
    _ARouter.navigation(context, postcard, requestCode, callback)
      ↓
    Step 1: 预处理回调
      NavigationCallback callback(可选)
      → onFound():找到路由
      → onLost():路由不存在
      → onArrival():跳转完成
      → onInterrupt():被拦截器中断
    
    Step 2: LogisticsCenter.completion(postcard)
      → 查找路由信息,填充Postcard
    
    Step 3: 拦截器处理
      if (!postcard.isGreenChannel()) {
          // 非绿色通道,需要经过拦截器
          interceptorService.doInterceptions(postcard, callback)
      }
    
    Step 4: 执行跳转
      _navigation(context, postcard, requestCode, callback)
      → 根据postcard.getType()分发:
      ├── ACTIVITY → buildIntent → startActivity
      ├── PROVIDER → 返回Provider实例
      ├── FRAGMENT → 实例化Fragment并设置参数
      └── 其他类型 → 对应处理
    
    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

    # 5.3 路由查找过程

    LogisticsCenter.completion(postcard) 详细过程:
    
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    
    if (routeMeta == null) {
        // 路由表中没有 → 尝试加载对应group
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
        
        if (groupMeta == null) {
            // 没有找到group → 路由不存在
            throw new NoRouteFoundException("没有找到路由: " + postcard.getPath());
        }
        
        // 实例化group类,加载该group的所有路由
        IRouteGroup group = groupMeta.getConstructor().newInstance();
        group.loadInto(Warehouse.routes);
        
        // 从groupsIndex中移除(已加载,避免重复加载)
        Warehouse.groupsIndex.remove(postcard.getGroup());
        
        // 重新查找
        completion(postcard);  // 递归调用
        return;
    }
    
    // 找到路由 → 填充Postcard
    postcard.setDestination(routeMeta.getDestination());
    postcard.setType(routeMeta.getType());
    postcard.setPriority(routeMeta.getPriority());
    postcard.setExtra(routeMeta.getExtra());
    
    // 如果是Provider类型,设置为绿色通道(不经过拦截器)
    if (routeMeta.getType() == RouteType.PROVIDER) {
        postcard.greenChannel();
    }
    
    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

    # 5.4 拦截器执行过程

    拦截器责任链执行:
    
    InterceptorServiceImpl.doInterceptions(postcard, callback):
    
    // 在子线程中执行拦截器
    executorService.execute(() -> {
        CancelableCountDownLatch counter = new CancelableCountDownLatch(1);
        
        _execute(0, counter, postcard);  // 从第0个拦截器开始
        
        // 等待拦截器链执行完毕(有超时机制)
        counter.await(postcard.getTimeout(), TimeUnit.SECONDS);
        
        if (counter.getCount() > 0) {
            // 超时 → 中断
            callback.onInterrupt("导航超时");
        }
    });
    
    _execute(int index, CountDownLatch counter, Postcard postcard):
      if (index < interceptors.size()) {
          IInterceptor interceptor = interceptors.get(index);
          interceptor.process(postcard, new InterceptorCallback() {
              @Override
              public void onContinue(Postcard postcard) {
                  // 放行 → 执行下一个拦截器
                  _execute(index + 1, counter, postcard);
              }
              
              @Override
              public void onInterrupt(Throwable exception) {
                  // 中断 → 停止执行
                  counter.cancel();
                  callback.onInterrupt(exception);
              }
          });
      } else {
          // 所有拦截器都放行 → 执行跳转
          counter.countDown();
      }
    
    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

    # 5.5 最终跳转过程

    _navigation()最终跳转:
    
    switch (postcard.getType()) {
        case ACTIVITY:
            // 构建Intent
            Intent intent = new Intent(context, postcard.getDestination());
            intent.putExtras(postcard.getExtras());
            
            // 设置flags
            int flags = postcard.getFlags();
            if (flags != 0) intent.setFlags(flags);
            
            // 在主线程执行跳转
            if (requestCode >= 0) {
                // startActivityForResult
                activity.startActivityForResult(intent, requestCode);
            } else {
                context.startActivity(intent);
            }
            
            // 转场动画
            if (postcard.getEnterAnim() >= 0 && postcard.getExitAnim() >= 0) {
                activity.overridePendingTransition(
                    postcard.getEnterAnim(), postcard.getExitAnim());
            }
            break;
            
        case PROVIDER:
            // 返回Provider实例(从缓存中获取或新建)
            return postcard.getProvider();
            
        case FRAGMENT:
            // 实例化Fragment
            Fragment fragment = postcard.getDestination().newInstance();
            fragment.setArguments(postcard.getExtras());
            return fragment;
    }
    
    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

    # 06.核心技术点思考

    # 6.1 为什么需要路由框架

    疑惑:Android原生的Intent跳转有什么不足?
    
    问题1:组件化后无法显式跳转
      模块间不互相依赖 → 无法引用对方的Activity类
      → Intent(context, OtherActivity.class) 编译报错
    
    问题2:隐式Intent存在问题
      → 需要在Manifest中声明intent-filter
      → 管理混乱,容易冲突
      → 不支持参数类型检查
      → 不支持拦截器
    
    问题3:缺乏统一的跳转管理
      → 无法统一处理降级策略
      → 无法统一添加登录拦截
      → 无法统一记录跳转埋点
    
    路由框架解决了这些问题:
      → URL映射替代类引用 → 解耦
      → 拦截器机制 → 统一拦截处理
      → 降级策略 → 路由失败兜底
      → 服务发现 → 跨模块服务调用
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    # 6.2 路由与原生跳转的对比

    路由 vs 原生Intent对比:
    
    | 特性          | 路由框架              | 原生Intent          |
    |---------------|----------------------|---------------------|
    | 跨模块跳转    | 支持(URL映射)       | 不支持(显式Intent) |
    | 拦截器        | 支持                  | 不支持              |
    | 降级策略      | 支持                  | 不支持              |
    | 参数自动注入  | 支持(@Autowired)    | 不支持              |
    | 服务发现      | 支持                  | 不支持              |
    | 编译时检查    | 部分支持              | 支持                |
    | 性能开销      | 略高(查表+反射)     | 低                  |
    | 学习成本      | 中等                  | 低                  |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 6.3 分组懒加载的设计

    为什么要分组懒加载?
    
    问题:大型App可能有数百个路由
      如果init时全量加载所有路由 → 启动变慢
      如果每次导航都扫描所有路由 → 导航变慢
    
    解决:两级索引 + 懒加载
      第一级:groupsIndex(Group索引)→ init时加载,量很小
      第二级:routes(具体路由)→ 按需加载,只在首次使用该group时
    
    类比:字典索引
      → groupsIndex 相当于目录(按拼音分组)
      → routes 相当于正文页面
      → 查字时先翻目录定位到对应页,再在页面中查找
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 6.4 降级策略设计

    路由找不到目标时的降级策略:
    
    方式1:全局降级(DegradeService)
      @Route(path = "/service/degrade")
      class DegradeServiceImpl implements DegradeService {
          @Override
          public void onLost(Context context, Postcard postcard) {
              // 全局降级处理
              // 如:跳转到404页面、弹Toast等
              Toast.makeText(context, "页面不存在", Toast.LENGTH_SHORT).show();
          }
      }
    
    方式2:单次降级(NavigationCallback)
      ARouter.getInstance()
          .build("/xxx/notexist")
          .navigation(context, new NavigationCallback() {
              @Override
              public void onLost(Postcard postcard) {
                  // 该次导航的降级处理
              }
          });
    
    优先级:单次降级 > 全局降级
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    # 6.5 多模块路由协作

    多模块路由协作机制:
    
    编译时:
      每个模块独立运行APT → 生成各自的路由注册类
      module-user → ARouter$$Root$$moduleuser + ARouter$$Group$$user
      module-shop → ARouter$$Root$$moduleshop + ARouter$$Group$$shop
      module-home → ARouter$$Root$$modulehome + ARouter$$Group$$home
    
    运行时:
      ARouter.init() → 扫描所有模块的注册类 → 统一加载到Warehouse
      → 所有模块的路由在同一个Warehouse中
      → 任何模块都可以导航到任何其他模块的页面
    
    注意事项:
      1. path必须全局唯一,不同模块不能使用相同path
      2. group名称建议与模块名对应,避免混淆
      3. 公共接口(IProvider)定义在base模块中
      4. 每个模块的ARouter注解处理器独立工作
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 07.ARouter与其他路由对比

    # 7.1 主流路由框架对比

    Android主流路由框架对比:
    
    | 特性          | ARouter       | TheRouter     | WMRouter      |
    |---------------|---------------|---------------|---------------|
    | 开发者        | 阿里巴巴     | 货拉拉        | 美团          |
    | 路由表生成    | APT          | APT + KSP     | APT           |
    | 初始化方式    | dex扫描/Plugin| Transform     | dex扫描       |
    | 分组加载      | 支持          | 支持          | 支持          |
    | 拦截器        | 支持          | 支持          | 支持          |
    | 服务发现      | 支持          | 支持          | 支持          |
    | 参数注入      | 支持          | 支持          | 不支持        |
    | Kotlin支持    | 一般          | 优秀          | 一般          |
    | KSP支持       | 不支持        | 支持          | 不支持        |
    | 动态路由      | 不支持        | 支持          | 支持          |
    | 维护状态      | 维护较少      | 活跃          | 一般          |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 7.2 TheRouter的改进

    TheRouter相比ARouter的改进:
    
    1. 编译速度优化
       ARouter: 使用APT(kapt对Kotlin项目编译速度影响大)
       TheRouter: 同时支持APT和KSP
       KSP编译速度比kapt快约2倍
    
    2. 初始化优化
       ARouter: 默认扫描dex,需要Gradle Plugin优化
       TheRouter: 编译时直接生成初始化代码,无需扫描
    
    3. 动态路由支持
       ARouter: 不支持运行时添加路由
       TheRouter: 支持动态添加和修改路由表
    
    4. 更好的Kotlin支持
       ARouter: 主要为Java设计
       TheRouter: 原生Kotlin设计
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 7.3 路由框架选型建议

    选型建议:
    
    小型项目(<5个模块):
      → 可以不用路由框架
      → 隐式Intent + 接口下沉足够
    
    中型项目(5-20个模块):
      → ARouter(成熟稳定,社区资源丰富)
      → TheRouter(如果使用Kotlin + KSP)
    
    大型项目(>20个模块):
      → TheRouter(更好的编译速度和扩展性)
      → 或自研路由框架(满足特定需求)
    
    注意:
      路由框架会增加学习成本和维护复杂度
      不要为了用路由而用路由
      先明确是否真正需要组件化
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 08.优秀代码设计深度解析

    # 8.1 设计模式应用全景

    ARouter作为一个成熟的路由框架,在架构设计中运用了多种经典设计模式:

    ARouter中的设计模式全景:
    
    1. 门面模式(Facade)
       ARouter类是整个框架的门面,对外暴露简洁的API
       → ARouter.init() / build() / navigation() / inject()
       → 内部所有复杂逻辑委托给_ARouter处理
       → 用户只需要与ARouter交互,无需了解内部实现
       
       public final class ARouter {
           public static void init(Application application) {
               // 实际工作委托给_ARouter
               if (!hasInit) {
                   _ARouter.init(application);
                   hasInit = true;
               }
           }
           
           public Postcard build(String path) {
               return _ARouter.getInstance().build(path);
           }
       }
       
       设计价值:
       → 隐藏了Warehouse、LogisticsCenter等内部类
       → 降低了使用门槛
       → ARouter可以随时替换内部实现而不影响使用者
    
    2. 责任链模式(Interceptor Chain)
       拦截器按priority排序形成链:
       LoginInterceptor(1) → PermissionInterceptor(2) → TrackInterceptor(3)
       
       每个拦截器处理后决定:
       → onContinue() → 传递给下一个拦截器
       → onInterrupt() → 中断整条链
       
       与OkHttp拦截器链的区别:
       → OkHttp:同步责任链,链式调用chain.proceed()
       → ARouter:异步责任链,通过callback回调
       → ARouter拦截器在子线程执行,适合异步操作(如网络检查登录态)
    
    3. 工厂模式(APT代码生成)
       APT生成的IRouteGroup就是路由元数据的工厂:
       ARouter$$Group$$user.loadInto(atlas)
       → 批量生产RouteMeta对象并注册到路由表
       → 每个module独立生成自己的工厂类
       → 运行时按需调用工厂方法加载路由
    
    4. 策略模式(RouteType分发)
       不同的RouteType使用不同的导航策略:
       ACTIVITY → 构建Intent + startActivity
       FRAGMENT → 反射实例化 + 设置Arguments
       PROVIDER → 获取/创建单例 + 调用init
       
       _navigation()中的switch-case就是策略选择器
    
    5. 单例模式(多层次单例)
       ARouter.getInstance() → 双重检查锁单例
       _ARouter.getInstance() → 双重检查锁单例
       Provider实例 → Warehouse缓存实现的单例
       InterceptorServiceImpl → 通过路由发现的单例服务
       → 多层次单例保证了框架的全局一致性
    
    6. 建造者模式(Postcard)
       Postcard支持链式参数配置:
       ARouter.getInstance()
           .build("/user/login")
           .withString("name", "yang")
           .withInt("age", 25)
           .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
           .withTransition(R.anim.enter, R.anim.exit)
           .navigation();
       → 将复杂的导航参数构建过程链式化
       → 每个with方法返回自身(Postcard)
    
    7. 模板方法模式(AbstractProcessor)
       APT处理器继承AbstractProcessor,重写process()方法:
       → init():初始化处理环境
       → getSupportedAnnotationTypes():声明处理哪些注解
       → process():核心处理逻辑(子类实现)
       → Java编译器驱动整个处理流程(模板方法)
    
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80

    # 8.2 Postcard继承体系设计

    Postcard的继承设计是ARouter中一个优秀的面向对象设计案例:
    
    RouteMeta(路由元数据)
      ├── type: RouteType        → 路由类型
      ├── destination: Class<?>  → 目标类
      ├── path: String           → 路由路径
      ├── group: String          → 路由分组
      ├── priority: int          → 优先级
      ├── extra: int             → 额外标记
      └── paramsType: Map        → 参数类型映射
    
        ↑ extends
    
    Postcard(导航明信片)
      ├── uri: Uri               → URI
      ├── tag: Object            → 标记
      ├── mBundle: Bundle        → 携带参数
      ├── flags: int             → Intent flags
      ├── timeout: int           → 超时时间
      ├── provider: IProvider    → Provider实例
      ├── greenChannel: boolean  → 是否跳过拦截器
      ├── enterAnim/exitAnim     → 转场动画
      └── optionsCompat: Bundle  → ActivityOptions
    
    设计精髓分析:
    
    1. 为什么Postcard继承RouteMeta而不是包含RouteMeta?
       → 继承使得Postcard天然具备路由元数据
       → completion()时可以直接将RouteMeta属性设置到Postcard
       → 避免了postcard.getRouteMeta().getDestination()的嵌套调用
       → 简化了后续代码中对路由信息的访问
    
       // LogisticsCenter.completion()中的属性赋值
       postcard.setDestination(routeMeta.getDestination());
       postcard.setType(routeMeta.getType());
       postcard.setPriority(routeMeta.getPriority());
       postcard.setExtra(routeMeta.getExtra());
       // 继承使得这些setter操作自然流畅
    
    2. RouteMeta是编译时数据,Postcard是运行时数据
       → RouteMeta由APT生成,存储静态路由信息
       → Postcard在导航时创建,附加动态参数
       → 继承体系让静态信息和动态信息融为一体
       → 一个Postcard对象包含了导航所需的所有信息
    
    3. Postcard的链式API设计
       每个with方法返回this(Postcard类型):
       public Postcard withString(String key, String value) {
           mBundle.putString(key, value);
           return this;
       }
       
       → 流式API,代码可读性好
       → 所有参数配置和路由信息在同一个对象上
       → navigation()方法在Postcard上直接调用
    
    4. greenChannel的设计
       Provider和Fragment类型自动设置greenChannel=true
       → 跳过拦截器,因为它们不是页面跳转
       → 用户也可以手动调用greenChannel()跳过拦截器
       → 如:跳转到关于页面、设置页面等不需要登录检查的页面
    
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61

    # 8.3 Warehouse单例仓库核心设计

    // Warehouse — ARouter的核心数据仓库
    class Warehouse {
        // Group索引:group名 → Group加载类
        static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
        
        // 路由表:path → RouteMeta(按需加载)
        static Map<String, RouteMeta> routes = new HashMap<>();
        
        // Provider索引:Provider接口Class → RouteMeta
        static Map<Class, RouteMeta> providersIndex = new HashMap<>();
        
        // Provider缓存:Provider接口Class → Provider实例
        static Map<Class, IProvider> providers = new HashMap<>();
        
        // 拦截器索引:priority → 拦截器类
        static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = 
            new UniqueKeyTreeMap<>("...");
        
        // 拦截器实例列表(已按priority排序)
        static List<IInterceptor> interceptors = new ArrayList<>();
        
        static void clear() {
            routes.clear();
            groupsIndex.clear();
            providers.clear();
            providersIndex.clear();
            interceptors.clear();
            interceptorsIndex.clear();
        }
    }
    
    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
    Warehouse设计深度分析:
    
    1. 为什么全部使用static字段?
       → Warehouse作为全局唯一的数据仓库
       → 静态字段保证了进程内唯一性
       → 不需要实例化,任何地方直接访问
       → 配合ARouter的单例模式,整个框架只有一份数据
    
    2. 双Map设计(providers vs providersIndex)
       providersIndex: Class<IProvider接口> → RouteMeta
         → 存储接口到路由元数据的映射
         → 用于通过接口类型查找Provider
       
       providers: Class<IProvider接口> → IProvider实例
         → 缓存已实例化的Provider
         → 避免重复创建(单例效果)
       
       查找流程:
       navigation(IUserService.class)
         → 先查providers缓存
         ├── 命中 → 直接返回实例
         └── 未命中 → 从providersIndex找到RouteMeta
             → 实例化Provider → init() → 存入providers → 返回
    
    3. UniqueKeyTreeMap的精妙选择
       拦截器使用UniqueKeyTreeMap存储:
       → TreeMap保证按Key(priority)自然排序
       → Unique保证不允许相同priority
       → 如果两个拦截器priority相同 → 抛异常
       → 编译时/运行时及早发现配置错误
    
    4. groupsIndex的"加载后删除"策略
       // 加载完group后从索引中移除
       Warehouse.groupsIndex.remove(postcard.getGroup());
       
       为什么?
       → 已加载的group不需要再次加载
       → 删除后下次直接从routes中查找
       → 节省内存(Class对象引用)
       → completion()中用是否存在于groupsIndex判断是否需要加载
    
    5. clear()方法的防御设计
       → 在ARouter.destroy()时调用
       → 清空所有数据,释放引用
       → 防止内存泄漏(尤其是Provider持有Context时)
       → 应用退出时应该调用
    
    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
    45
    46

    # 8.4 LogisticsCenter路由补全算法

    LogisticsCenter(物流中心)是ARouter的核心调度类,
    completion()方法是其最重要的算法:
    
    LogisticsCenter.completion(Postcard postcard):
    
    Step 1: 查找路由
      RouteMeta routeMeta = Warehouse.routes.get(path);
      
    Step 2: 路由不存在 → 尝试懒加载
      if (routeMeta == null) {
          Class<? extends IRouteGroup> groupMeta = 
              Warehouse.groupsIndex.get(group);
          
          if (groupMeta == null) {
              // group也不存在 → 路由确实不存在
              // 尝试降级处理
              if (degradeService != null) {
                  degradeService.onLost(context, postcard);
              }
              return;
          }
          
          // 懒加载该group的所有路由
          groupMeta.getConstructor().newInstance().loadInto(Warehouse.routes);
          // 从索引中移除已加载的group
          Warehouse.groupsIndex.remove(group);
          // 递归重新completion(这次routes中一定有了)
          completion(postcard);
      }
    
    Step 3: 路由存在 → 填充Postcard
      postcard.setDestination(routeMeta.getDestination());
      postcard.setType(routeMeta.getType());
      ...
    
    Step 4: 类型特殊处理
      switch (routeMeta.getType()) {
          case PROVIDER:
              // Provider类型:实例化并缓存
              Class<? extends IProvider> providerMeta = 
                  (Class<? extends IProvider>) routeMeta.getDestination();
              IProvider instance = Warehouse.providers.get(providerMeta);
              if (instance == null) {
                  instance = providerMeta.getConstructor().newInstance();
                  instance.init(mContext);
                  Warehouse.providers.put(providerMeta, instance);
              }
              postcard.setProvider(instance);
              postcard.greenChannel();  // Provider不经过拦截器
              break;
          case FRAGMENT:
              postcard.greenChannel();  // Fragment也不经过拦截器
              break;
      }
    
    算法设计亮点:
    
    1. 递归补全的优雅设计
       completion() → 发现需要懒加载 → 加载 → 递归调用自己
       → 第二次调用时routes中已有数据 → 正常流程
       → 避免了代码重复(加载后不需要复制同样的填充逻辑)
       → 递归深度最多2层,不会栈溢出
    
    2. 懒加载的触发时机
       不是在init()时加载所有路由
       → 而是在首次导航到某个group时才加载该group
       → 典型的延迟初始化策略
       → 对包含数百个路由的大型App尤为重要
    
    3. 降级策略的优先级
       路由不存在时:
       → 先检查DegradeService(全局降级)
       → NavigationCallback.onLost()(单次降级)
       → 两者可以共存,单次优先
       
    4. Provider的双重缓存
       → 先查Warehouse.providers(实例缓存)
       → 未命中才实例化
       → init()只调用一次
       → 保证Provider的单例语义
    
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80

    # 8.5 拦截器CancelableCountDownLatch设计

    // CancelableCountDownLatch — ARouter自定义的可取消倒计时锁
    // 继承自CountDownLatch,增加了cancel能力
    public class CancelableCountDownLatch extends CountDownLatch {
        
        public CancelableCountDownLatch(int count) {
            super(count);
        }
        
        // 取消等待,立即释放所有等待线程
        public void cancel() {
            while (getCount() > 0) {
                countDown();  // 将计数器减到0
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    CancelableCountDownLatch在拦截器链中的应用:
    
    InterceptorServiceImpl.doInterceptions():
      CancelableCountDownLatch counter = new CancelableCountDownLatch(1);
      
      // 在子线程异步执行拦截器链
      _execute(0, counter, postcard);
      
      // 主导航线程等待拦截器链完成
      counter.await(timeout, TimeUnit.SECONDS);
      
      if (counter.getCount() > 0) {
          // 超时未完成 → 导航超时
      }
    
    三种结束方式:
      1. 所有拦截器放行 → 最后一个拦截器后countDown() → await返回
      2. 某个拦截器中断 → cancel() → countDown到0 → await返回
      3. 超时 → await自动返回 → getCount() > 0 标识超时
    
    设计优势分析:
    
    1. 为什么用CountDownLatch而不是wait/notify?
       → CountDownLatch语义更清晰:等待一个事件完成
       → 自带超时机制:await(timeout, unit)
       → 不会出现虚假唤醒问题
       → 一次性使用,不需要重置
    
    2. 为什么需要cancel()方法?
       标准CountDownLatch没有cancel
       → 拦截器中断时需要立即唤醒等待线程
       → cancel()通过循环countDown实现
       → 比Thread.interrupt()更温和安全
    
    3. 为什么计数器是1而不是拦截器数量?
       → 拦截器链是串行的(异步递归执行)
       → 不是并行等待多个拦截器完成
       → 只需要等待整条链的最终结果
       → count=1表示"等待链执行完毕"这一个事件
    
    4. 超时机制的重要性
       → 拦截器在子线程中异步执行
       → 如果某个拦截器既不onContinue也不onInterrupt → 卡住
       → 超时机制保证导航不会永远阻塞
       → 默认300秒(可通过Postcard.setTimeout()配置)
       → 这是防御性编程的典型案例
    
    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
    45
    46

    # 8.6 JavaPoet代码生成设计

    ARouter使用JavaPoet库在编译时生成Java代码,
    这是APT处理器中代码生成的最佳实践:
    
    JavaPoet核心API:
      TypeSpec → 生成类定义
      MethodSpec → 生成方法定义
      FieldSpec → 生成字段定义
      CodeBlock → 生成代码块
      ParameterSpec → 生成参数定义
      JavaFile → 写入文件
    
    RouteProcessor中的代码生成示例:
    
    // 生成loadInto方法
    MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto")
        .addAnnotation(Override.class)
        .addModifiers(Modifier.PUBLIC)
        .addParameter(
            ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouteMeta.class)
            ),
            "atlas"
        );
    
    // 为每个路由生成注册代码
    for (RouteMeta routeMeta : routeMetaList) {
        loadIntoMethodBuilder.addStatement(
            "atlas.put($S, $T.build($T.$L, $T.class, $S, $S, null, $L, $L))",
            routeMeta.getPath(),
            ClassName.get(RouteMeta.class),
            ClassName.get(RouteType.class),
            routeMeta.getType(),
            ClassName.get(routeMeta.getDestination()),
            routeMeta.getPath(),
            routeMeta.getGroup(),
            routeMeta.getPriority(),
            routeMeta.getExtra()
        );
    }
    
    // 生成类
    TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName)
        .addSuperinterface(ClassName.get(IRouteGroup.class))
        .addModifiers(Modifier.PUBLIC)
        .addMethod(loadIntoMethodBuilder.build())
        .build();
    
    // 写入文件
    JavaFile.builder(ROUTE_PACKAGE, typeSpec)
        .build()
        .writeTo(mFiler);
    
    JavaPoet设计优势:
      1. 类型安全:通过ClassName、TypeName等避免拼字符串
         → 不会出现拼错类名、包名的问题
         → 重构时IDE可以追踪引用
      
      2. 自动import管理
         → JavaFile自动收集所有引用的类型
         → 自动生成import语句
         → 不会遗漏或重复
      
      3. $S/$T/$L占位符
         → $S: 字符串字面量(自动加引号)
         → $T: 类型引用(自动import)
         → $L: 字面量(原样输出)
         → 简洁且不易出错
      
      4. 相比直接拼StringBuilder
         → 可读性好:代码意图清晰
         → 维护性好:修改一处自动影响关联部分
         → 正确性好:格式、缩进自动处理
    
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74

    # 8.7 两级索引架构思想

    ARouter的两级索引是其性能优化的核心设计:
    
    一级索引(Group Index) — init时加载
      groupsIndex: {
          "user"    → ARouter$$Group$$user.class
          "shop"    → ARouter$$Group$$shop.class
          "home"    → ARouter$$Group$$home.class
          "setting" → ARouter$$Group$$setting.class
      }
      → 数据量很小(只有group数量,通常10-20个)
      → Class对象引用,内存占用极小
      → O(1)查找
    
    二级索引(Routes) — 按需加载
      routes: {
          "/user/login"    → RouteMeta(ACTIVITY, LoginActivity.class, ...)
          "/user/register" → RouteMeta(ACTIVITY, RegisterActivity.class, ...)
          "/user/detail"   → RouteMeta(ACTIVITY, UserDetailActivity.class, ...)
          // shop和home的路由还未加载...
      }
      → 只有被使用到的group的路由才会加载
      → 每个RouteMeta包含完整路由信息
      → 加载后的路由O(1)查找
    
    性能分析(假设应用有200个路由,20个group):
    
    全量加载方式:
      init()时加载200个RouteMeta → 创建200个对象 → 200次Map.put
      内存:200 × RouteMeta对象大小
      时间:遍历所有路由注册类 + 200次put
    
    两级索引方式:
      init()时只加载20个Group索引 → 20个Class引用 → 20次Map.put
      导航到某个group时才加载该group的路由(平均10个)
      
      如果用户只访问了3个group:
      → 实际加载:20 + 3×10 = 50个Map条目
      → 比全量加载减少75%
    
    类比理解:
      全量加载 = 开机时把整本字典读入内存
      两级索引 = 开机时只加载目录,翻到某页时才读该页内容
      → 大幅降低初始化时间和内存占用
    
    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

    # 8.8 _ARouter门面模式设计

    ARouter与_ARouter的双层设计是门面模式的典型应用:
    
    外层:ARouter(公开API,对用户可见)
      public final class ARouter {
          private volatile static ARouter instance = null;
          private volatile static boolean hasInit = false;
          
          public static void init(Application application) {
              if (!hasInit) {
                  _ARouter.init(application);
                  hasInit = true;
              }
          }
          
          public Postcard build(String path) {
              return _ARouter.getInstance().build(path);
          }
          
          public <T> T navigation(Class<? extends T> service) {
              return _ARouter.getInstance().navigation(service);
          }
      }
    
    内层:_ARouter(实际实现,包级别访问)
      final class _ARouter {
          // 所有真正的逻辑都在这里
          static void init(Application application) { ... }
          protected Postcard build(String path) { ... }
          protected Object navigation(Context ctx, Postcard postcard, ...) { ... }
      }
    
    这种双层设计的价值:
    
    1. 接口稳定性
       → ARouter的公开方法是稳定的API
       → _ARouter的内部实现可以随时重构
       → 只要ARouter的接口不变,所有使用者不受影响
    
    2. 访问控制
       → ARouter是public的,用户可以直接使用
       → _ARouter是包级别的,用户无法直接访问
       → 防止用户绕过ARouter直接操作内部逻辑
       → 内部类、内部方法不会出现在IDE的自动补全中
    
    3. 日志和异常的统一处理
       ARouter在委托调用时可以统一添加:
       → try-catch包裹(捕获内部异常,友好提示)
       → 日志记录(debug模式下输出导航信息)
       → 线程安全检查(init必须在主线程等)
    
    4. 初始化状态管理
       → hasInit在ARouter层管理
       → 所有API调用前检查初始化状态
       → 未初始化时给出清晰的错误信息
       → _ARouter可以假设已初始化,简化内部逻辑
    
    这种设计模式在Android开发中很常见:
      → Glide(Glide vs RequestManager)
      → Retrofit(Retrofit vs ServiceMethod)
      → 对外简洁,对内复杂
    
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60

    # 8.9 Provider单例缓存设计

    Provider的单例缓存是ARouter中服务发现的关键设计:
    
    Provider生命周期管理:
    
    1. 首次获取
       navigation(IUserService.class)
       → LogisticsCenter.completion()
       → 检查Warehouse.providers是否有缓存
       → 没有 → 反射实例化 → 调用init(context) → 存入缓存
    
    2. 后续获取
       navigation(IUserService.class)
       → LogisticsCenter.completion()
       → 从Warehouse.providers获取缓存实例 → 直接返回
    
    3. 销毁
       ARouter.destroy() → Warehouse.clear() → providers.clear()
    
    单例设计的深度思考:
    
    疑惑:Provider为什么默认是单例的?
    
    答疑:
      1. 服务通常是无状态的工具类
         → IUserService.getUserInfo()
         → IPayService.pay()
         → 不需要多个实例
    
      2. 避免重复初始化的开销
         → init(context)可能包含耗时操作
         → 如初始化数据库连接、网络配置等
         → 单例避免了重复初始化
    
      3. 与Android Service的语义一致
         → Android的Service也是单例的
         → Provider在概念上等同于"轻量级服务"
         → 保持了语义一致性
    
    Provider初始化的线程安全问题:
    
    // LogisticsCenter中的Provider实例化代码
    IProvider provider = Warehouse.providers.get(providerMeta);
    if (provider == null) {
        provider = providerMeta.getConstructor().newInstance();
        provider.init(mContext);
        Warehouse.providers.put(providerMeta, provider);
    }
    
    潜在问题:这段代码不是线程安全的
      → 两个线程同时navigation同一个Provider
      → 可能创建两个实例,只有一个被缓存
      → init()可能被调用两次
    
    实际影响:
      → 因为completion()在navigation()中有synchronized保护
      → 大多数场景下不会出现并发问题
      → 但在极端场景下仍需注意
      → 这也是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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58

    # 8.10 设计思想总结

    ARouter核心设计思想归纳:
    
    1. 编译时 vs 运行时的权衡
       APT在编译时生成代码 → 运行时零反射开销
       Gradle Plugin在编译时扫描 → 运行时无需遍历dex
       → 核心思想:把能在编译时做的工作都在编译时做完
       → 运行时只做查表和分发,效率极高
    
    2. 分层解耦架构
       ARouter(API层)→ _ARouter(实现层)→ Warehouse(数据层)
       → 每层职责清晰
       → 对外API稳定,内部实现可自由演化
       → 门面模式降低了使用复杂度
    
    3. 懒加载优先的策略
       路由表分组懒加载 → 按需加载
       Provider延迟实例化 → 首次使用时创建
       拦截器init在框架init时完成 → 唯一的例外,因为拦截器需要全局可用
       → 不到万不得已不加载,不到万不得已不创建
    
    4. 约定优于配置
       path的第一段自动作为group名 → 无需手动指定group
       Provider注解的path自动关联接口类型 → 无需额外配置
       @Autowired的参数名默认与字段名相同 → 减少配置
       → 减少样板代码,提升开发效率
    
    5. 防御性编程
       拦截器超时机制 → 防止导航永远阻塞
       路由不存在降级策略 → 优雅处理异常
       重复注册检测 → 编译时/运行时双重检查
       → 每个环节都有兜底方案
    
    6. 面向接口编程
       IProvider → 服务接口下沉到公共模块
       IRouteGroup → 路由注册接口
       IInterceptor → 拦截器接口
       → 所有扩展点都是接口
       → 框架与使用者通过接口契约交互
    
    7. 关注点分离
       RouteProcessor → 只处理路由注解
       InterceptorProcessor → 只处理拦截器注解
       AutowiredProcessor → 只处理参数注入
       LogisticsCenter → 只负责路由查找和补全
       InterceptorServiceImpl → 只负责拦截器执行
       → 每个类只做一件事(单一职责原则)
    
    8. URL驱动的松耦合
       模块间不依赖具体类 → 依赖URL字符串
       URL可以在运行时动态构建 → 支持Deep Link
       URL可以在配置中心管理 → 支持动态路由(TheRouter)
       → 从类级别耦合降级为字符串级别耦合
       → 这是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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53

    # 09.面试高频问题深度解析

    # 9.1 经典面试题

    问题1:ARouter的工作原理是什么?

    核心原理:APT编译时生成路由表 + 运行时查表导航
    
    1. 编译时
       APT扫描@Route注解
       → 为每个模块生成路由注册类(包含path→Class的映射)
    
    2. 初始化时
       扫描dex/通过Plugin加载所有路由注册类
       → 将Group索引加载到内存(分组懒加载)
    
    3. 导航时
       build(path) → 创建Postcard
       navigation() → 根据path查找目标Class
       → 执行拦截器链 → 构建Intent → startActivity()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    问题2:ARouter的拦截器是如何设计的?

    责任链模式:
    → 所有拦截器按priority排序
    → 在子线程中依次执行
    → 每个拦截器必须调用onContinue()放行或onInterrupt()中断
    → 有超时机制防止卡住(默认300秒)
    → Fragment跳转默认不经过拦截器(isGreenChannel)
    
    1
    2
    3
    4
    5
    6

    # 9.2 进阶面试题

    问题3:ARouter初始化为什么慢?如何优化?

    原因:
    默认通过扫描dex文件查找APT生成的路由类
    → 遍历所有类名 → 过滤以特定前缀开头的类
    → 大型项目类数以万计 → 首次扫描耗时1-3秒
    
    优化方案:
    1. ARouter自带缓存(第二次启动会用缓存)
    2. arouter-register Gradle Plugin
       → 编译时通过ASM字节码插桩
       → 将注册代码直接写入init()方法
       → 运行时无需扫描dex
       → 初始化从秒级降到毫秒级
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    问题4:ARouter的服务发现是如何实现的?

    1. 公共模块定义接口(继承IProvider)
    2. 业务模块实现接口并添加@Route注解
    3. APT生成路由注册代码,将接口→实现类映射存入Warehouse
    4. 其他模块通过ARouter.navigation(IService.class)获取实例
    5. Provider默认是单例模式,首次获取时实例化并缓存
    
    1
    2
    3
    4
    5

    问题5:为什么路由框架使用APT而不是运行时反射?

    APT vs 运行时反射:
    
    APT优势:
    ├── 编译时生成代码,零运行时开销
    ├── 编译时可以检查注解错误
    ├── 生成的代码可以被ProGuard优化
    └── 不影响应用启动速度
    
    运行时反射劣势:
    ├── 启动时需要扫描所有类 → 慢
    ├── 反射调用性能差
    ├── 无法在编译时检查错误
    └── 不利于代码混淆
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 9.3 学习建议

    学习ARouter建议按以下顺序:

    1. 理解组件化:先理解为什么需要组件化,组件化的核心问题是什么
    2. 掌握APT:学习注解处理器的工作原理,理解编译时代码生成
    3. 使用ARouter:在实际项目中使用ARouter,熟悉API
    4. 阅读源码:从init() → build() → navigation()三条主线阅读源码
    5. 理解设计:分组懒加载、拦截器责任链、服务发现等设计思想
    6. 横向对比:了解TheRouter等替代方案,理解各自的优缺点

    # 参考博客

    • ARouter官方文档
      • https://github.com/alibaba/ARouter
    • ARouter源码分析
      • https://juejin.cn/post/6844903560826060814
    上次更新: 2026/06/10, 11:13:41
    EventBus事件总设计
    系统启动Zygote

    ← EventBus事件总设计 系统启动Zygote→

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