ARouter 源码架构与内核原理
上一篇文章介绍了 ARouter 的使用方式——从页面路由到参数注入、从拦截器到服务发现。本文将深入 ARouter 的源码内部,逐层剖析它的架构设计与实现机制。我们会回答以下核心问题:
@Route注解是在编译期怎样被扫描、怎样生成路由映射类的?ARouter.init()究竟做了什么?路由表是如何加载到内存的?build("/path").navigation()这一行代码的背后,经历了多少个处理环节?arouter-register插件是如何通过字节码插桩消除运行时 Dex 扫描的?
一、 整体架构:三大工件的分工
ARouter 的源码分为三个层次清晰的工件,各自工作在不同的编译阶段:
┌─────────────────────────────────────────────────────────┐
│ 编译期 │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ arouter-annotation│ │ arouter-compiler │ │
│ │ │ │ │ │
│ │ @Route │◀───│ RouteProcessor │ │
│ │ @Interceptor │ │ InterceptorProcessor │ │
│ │ @Autowired │ │ AutowiredProcessor │ │
│ │ RouteMeta │ │ │ │
│ │ RouteType │ │ 扫描注解 → JavaPoet 生成 │ │
│ └──────────────────┘ │ ARouter$$Group$$xxx.java │ │
│ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ arouter-register (Gradle 插件) │ │
│ │ │ │
│ │ Transform API → 扫描 .class → ASM 字节码插桩 │ │
│ │ 将生成的路由类注册代码注入到 LogisticsCenter │ │
│ └──────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 运行期 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ arouter-api │ │
│ │ │ │
│ │ ARouter (门面) → _ARouter (核心实现) │ │
│ │ Warehouse (路由仓库) │ │
│ │ LogisticsCenter (物流中心) │ │
│ │ InterceptorService (拦截器服务) │ │
│ │ Postcard (路由请求载体) │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
这三层的关系可以用一个物流体系来类比:
arouter-compiler就像印刷厂——在"发货前"(编译期)就把所有的配送地址册(路由映射类)印好。arouter-register是装配工——在包装环节(打包期)把地址册直接塞进配送中心的系统里。arouter-api是配送中心——运行时收到"寄件请求"(navigation()),立刻查表、过安检(拦截器)、送达目的地。
二、 编译期:注解处理器如何生成路由表
2.1 注解处理器的入口
ARouter 的编译期处理由 arouter-compiler 模块中的三个注解处理器完成:
| 处理器 | 负责的注解 | 生成的产物 |
|---|---|---|
RouteProcessor |
@Route |
路由映射类(ARouter$$Group$$xxx, ARouter$$Root$$xxx) |
InterceptorProcessor |
@Interceptor |
拦截器映射类(ARouter$$Interceptors$$xxx) |
AutowiredProcessor |
@Autowired |
参数注入类(xxx$$ARouter$$Autowired) |
所有处理器都继承自 BaseProcessor,它在 init() 方法中从 Gradle 的 arguments 中读取 AROUTER_MODULE_NAME——这就是为什么每个模块都必须配置这个参数。
2.2 RouteProcessor 的核心流程
RouteProcessor 是 ARouter 编译期处理的核心。当编译器遇到 @Route 注解时,整个处理流程如下:
编译开始
│
▼
RouteProcessor.process() 被调用
│
▼
roundEnv.getElementsAnnotatedWith(Route.class)
│ 获取所有标注了 @Route 的类元素
▼
遍历每个元素,解析注解属性:
├── path = "/order/detail"
├── group = "order"(自动从 path 第一段提取)
└── extras = 0x01
│
▼
构建 RouteMeta 对象,按 group 分组存入 Map<String, Set<RouteMeta>>
│
│ groupMap:
│ ┌─────────────────────────────────────────┐
│ │ "order" → [ │
│ │ RouteMeta("/order/detail", OrderDetailActivity.class, ACTIVITY),
│ │ RouteMeta("/order/list", OrderListActivity.class, ACTIVITY),
│ │ ] │
│ │ "user" → [ │
│ │ RouteMeta("/user/login", LoginActivity.class, ACTIVITY),
│ │ RouteMeta("/user/service", UserServiceImpl.class, PROVIDER),
│ │ ] │
│ └─────────────────────────────────────────┘
│
▼
使用 JavaPoet 为每个 group 生成一个 IRouteGroup 实现类
│
▼
生成一个 IRouteRoot 实现类,索引所有 group
2.3 生成代码的具体形态
假设我们的 module_order 模块中有以下路由声明:
@Route(path = "/order/detail")
class OrderDetailActivity : AppCompatActivity() { ... }
@Route(path = "/order/list", extras = 0x01)
class OrderListActivity : AppCompatActivity() { ... }
编译后,APT 会生成两个核心类:
1. 分组路由类:ARouter$$Group$$order
/**
* 编译时自动生成——order 分组的路由加载器
* 实现 IRouteGroup 接口,内部硬编码了 path → RouteMeta 的映射
*/
public class ARouter$$Group$$order implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
// 将 /order/detail 映射到 OrderDetailActivity
atlas.put("/order/detail",
RouteMeta.build(
RouteType.ACTIVITY, // 路由类型
OrderDetailActivity.class, // 目标类
"/order/detail", // 路径
"order", // 分组
null, // 参数类型映射(用于参数注入)
-1, // 优先级
0 // extras
)
);
// 将 /order/list 映射到 OrderListActivity
atlas.put("/order/list",
RouteMeta.build(
RouteType.ACTIVITY,
OrderListActivity.class,
"/order/list",
"order",
null,
-1,
1 // extras = NEED_LOGIN
)
);
}
}
2. 根索引类:ARouter$$Root$$module_order
/**
* 编译时自动生成——module_order 模块的路由根索引
* 实现 IRouteRoot 接口,记录该模块有哪些分组
*/
public class ARouter$$Root$$module_order implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
// 记录 "order" 分组 → 对应的加载器类
routes.put("order", ARouter$$Group$$order.class);
}
}
为什么要分两层(Root + Group)? 这是 ARouter 的按需加载设计:
初始化时只加载 Root 层(极轻量):
"order" → ARouter$$Group$$order.class (仅记录 Class,不实例化)
"user" → ARouter$$Group$$user.class
首次访问 /order/xxx 时才加载 Group 层:
实例化 ARouter$$Group$$order,调用 loadInto()
将 /order/detail → OrderDetailActivity 等映射加载到内存
效果:100 个分组的 App,如果用户只访问了 3 个分组,
则只有 3 个分组的路由表被实际加载到内存
2.4 AutowiredProcessor 的生成逻辑
对于 @Autowired 注解的字段,APT 会生成一个"注射器"类:
/**
* 编译时自动生成——OrderDetailActivity 的参数注入器
* 实现 ISyringe 接口
*/
public class OrderDetailActivity$$ARouter$$Autowired implements ISyringe {
@Override
public void inject(Object target) {
OrderDetailActivity substitute = (OrderDetailActivity) target;
// 直接调用 getIntent().getXxx()——与手写代码完全一致的性能
substitute.orderId = substitute.getIntent().getStringExtra("orderId");
substitute.fromSource = substitute.getIntent().getIntExtra("source", 0);
// 对于自定义对象,使用 SerializationService 进行反序列化
if (null != serializationService) {
substitute.orderInfo = serializationService.parseObject(
substitute.getIntent().getStringExtra("orderInfo"),
new com.alibaba.android.arouter.facade.model.TypeWrapper<OrderInfo>(){}.getType()
);
}
}
}
调用链很短:
ARouter.getInstance().inject(this)
→ _ARouter.inject(this)
→ 通过 this.getClass().getName() + "$$ARouter$$Autowired" 拼接类名
→ Class.forName() 加载注射器类
→ 实例化并调用 inject(target)
这里使用了一次性的反射来找到注射器类,但注射器类本身执行的代码是纯 getter 调用,没有任何反射——这是一个巧妙的性能平衡。
2.5 InterceptorProcessor 的生成逻辑
@Interceptor 注解的处理器生成的代码结构类似:
/**
* 编译时自动生成——拦截器索引
*/
public class ARouter$$Interceptors$$module_user implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
// priority → 拦截器 Class
interceptors.put(8, LoginInterceptor.class);
}
}
拦截器的加载与路由表不同——它在 init() 时就全量加载并按优先级排序,因为每次路由跳转都可能需要执行全部拦截器。
三、 运行期:核心组件的协作
3.1 Warehouse:路由仓库
Warehouse 是 ARouter 运行时的静态内存数据库,所有路由信息最终都汇聚于此。它的内部结构非常清晰:
/**
* ARouter 的运行时数据仓库
* 所有字段均为 static,全局唯一
*/
class Warehouse {
// ============ 路由相关 ============
// 分组索引:group名 → 对应 IRouteGroup 的 Class
// 初始化时即加载,用于按需触发分组的路由加载
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
// 路由表:path → RouteMeta(包含目标 Class、类型、参数信息等)
// 按需加载——某个分组首次被访问时,才将该分组的路由信息填入此表
static Map<String, RouteMeta> routes = new HashMap<>();
// ============ 服务(Provider)相关 ============
// Provider 索引:服务名 → RouteMeta
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Provider 实例缓存:服务 Class → 实例
// Provider 是单例的,首次获取时反射创建,之后从缓存读取
static Map<Class, IProvider> providers = new HashMap<>();
// ============ 拦截器相关 ============
// 拦截器索引:priority → IInterceptor 的 Class
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex
= new UniqueKeyTreeMap<>("...");
// 拦截器实例列表:按优先级排序的拦截器实例
static List<IInterceptor> interceptors = new ArrayList<>();
}
设计亮点:UniqueKeyTreeMap
拦截器索引使用了 TreeMap 的变种 UniqueKeyTreeMap。TreeMap 天然按 Key(即 priority)排序,这意味着拦截器在存入时就已经完成了排序,运行时遍历执行时无需额外排序操作。而 Unique 的约束确保不会有两个拦截器使用相同的优先级——如果发生冲突,加载时就会抛出异常,将问题暴露在开发阶段。
3.2 ARouter 与 _ARouter:门面模式
ARouter 对外暴露的 ARouter 类实际上只是一个门面(Facade)——它的每个方法都直奔 _ARouter 的对应方法:
// ARouter.java(门面类)
public final class ARouter {
// 所有方法都委托给 _ARouter
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
public <T> T navigation(Class<? extends T> service) {
return _ARouter.getInstance().navigation(service);
}
public void inject(Object thiz) {
_ARouter.inject(thiz);
}
// ...
}
为什么要这么设计? 这是经典的门面模式 + 访问控制。_ARouter 包含大量内部实现细节(如 afterInit()、_navigation() 等),这些方法不应暴露给外部调用者。通过双层结构,ARouter 只暴露干净的公共 API,而 _ARouter 可以自由地组织内部逻辑。
3.3 LogisticsCenter:物流调度中心
LogisticsCenter 是 ARouter 运行时最核心的类,负责两件关键任务:
任务一:初始化——加载路由表
public class LogisticsCenter {
/**
* 初始化方法——将编译期生成的路由表加载到 Warehouse
*/
public synchronized static void init(Context context, ThreadPoolExecutor executor)
throws HandlerException {
// ===== 方式一:使用 arouter-register 插件(推荐) =====
// 如果使用了插件,下面的 loadRouterMap() 方法体已经被 ASM 插桩,
// 注入了类似以下的代码:
// register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_order");
// register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_user");
// register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$module_user");
// register("com.alibaba.android.arouter.routes.ARouter$$Providers$$module_order");
loadRouterMap();
// 检查插桩是否成功——如果 registerByPlugin 仍为 false,说明没使用插件
if (registerByPlugin) {
// 插桩已完成注册,直接返回
return;
}
// ===== 方式二:运行时扫描 Dex(fallback) =====
// 没有使用插件时,才走这条路径
Set<String> routerMap;
// 如果是调试模式或版本号变化,重新扫描
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// 扫描 APK 中所有 Dex 文件,找到包名以
// "com.alibaba.android.arouter.routes" 开头的所有类
routerMap = ClassUtils.getFileNameByPackageName(
mContext, ROUTE_ROOT_PACKAGE
);
// 缓存到 SharedPreferences,下次启动不再扫描
if (!routerMap.isEmpty()) {
context.getSharedPreferences(...)
.edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context);
} else {
// 非首次安装,从缓存读取
routerMap = context.getSharedPreferences(...)
.getStringSet(AROUTER_SP_KEY_MAP, new HashSet<>());
}
// 逐个加载扫描到的类
for (String className : routerMap) {
if (className.contains("$$Root$$")) {
// Root 类 → 加载分组索引到 Warehouse.groupsIndex
((IRouteRoot) Class.forName(className).getConstructor()
.newInstance()).loadInto(Warehouse.groupsIndex);
} else if (className.contains("$$Interceptors$$")) {
// 拦截器类 → 加载到 Warehouse.interceptorsIndex
((IInterceptorGroup) Class.forName(className).getConstructor()
.newInstance()).loadInto(Warehouse.interceptorsIndex);
} else if (className.contains("$$Providers$$")) {
// Provider 类 → 加载到 Warehouse.providersIndex
((IProviderGroup) Class.forName(className).getConstructor()
.newInstance()).loadInto(Warehouse.providersIndex);
}
}
}
}
任务二:路由补全(completion)
当 navigation() 发起跳转时,Postcard 中只有一个 path 字符串。LogisticsCenter.completion() 的任务是在 Warehouse 中查表,将目标类、类型、参数信息等"补全"到 Postcard 上:
/**
* 路由信息补全——从 path 查到完整的路由元数据
*/
public synchronized static void completion(Postcard postcard) {
// 第一步:从路由表中查找
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// 路由表中没找到——可能是该分组尚未加载
// 尝试从分组索引中加载对应的分组
Class<? extends IRouteGroup> groupMeta =
Warehouse.groupsIndex.get(postcard.getGroup());
if (null == groupMeta) {
// 分组索引中也没有——真的找不到目标
throw new NoRouteFoundException("没有找到路由: " + postcard.getPath());
}
// 实例化分组加载器,将该分组的所有路由加入 Warehouse.routes
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
// 加载完成后,从分组索引中移除(避免重复加载)
Warehouse.groupsIndex.remove(postcard.getGroup());
// 重新递归调用 completion——此时 routes 中一定有了
completion(postcard);
return;
}
// 第二步:将 routeMeta 中的信息填充到 postcard
postcard.setDestination(routeMeta.getDestination()); // 目标 Class
postcard.setType(routeMeta.getType()); // ACTIVITY / FRAGMENT / PROVIDER
postcard.setPriority(routeMeta.getPriority()); // 优先级
postcard.setExtra(routeMeta.getExtra()); // extras 标记位
// 第三步:特殊处理 Provider 类型
switch (routeMeta.getType()) {
case PROVIDER:
// Provider 需要立即实例化并缓存
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
try {
IProvider provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("初始化 Provider 失败: " + providerMeta);
}
}
postcard.setProvider(instance);
// Provider 默认走绿色通道——不经过拦截器
postcard.greenChannel();
break;
// FRAGMENT 也走绿色通道
case FRAGMENT:
postcard.greenChannel();
break;
default:
break;
}
}
按需加载的递归设计非常优雅:首次查找 miss → 加载对应分组 → 再查一次。这意味着:
- 如果你的 App 有 20 个分组,但用户只访问了 3 个页面涉及 2 个分组
- 内存中只会加载 2 个分组的路由数据
- 其余 18 个分组的路由加载器类(
ARouter$$Group$$xxx)甚至不会被实例化
四、 路由跳转的完整流程
4.1 全链路时序
从 ARouter.getInstance().build("/order/detail").navigation() 到 Activity 启动,完整的调用链如下:
ARouter.getInstance().build("/order/detail")
│
├── 1. _ARouter.build(path)
│ 创建 Postcard 对象,解析 path 和 group
│ postcard = new Postcard("/order/detail", "order")
│
▼
.withString("orderId", "123").navigation()
│
├── 2. _ARouter.navigation(postcard)
│ │
│ ├── 2.1 PretreatmentService.onPretreatment()(如果存在)
│ │ 全局预处理(埋点等),返回 false 则终止
│ │
│ ├── 2.2 LogisticsCenter.completion(postcard)
│ │ 查 Warehouse → 按需加载分组 → 填充目标 Class 等信息
│ │
│ ├── 2.3 判断是否走绿色通道
│ │ │
│ │ ├── 是(Provider / Fragment / 手动调用 greenChannel())
│ │ │ 直接进入 2.4
│ │ │
│ │ └── 否(普通 Activity 跳转)
│ │ 进入 InterceptorService.doInterceptions()
│ │ 按优先级遍历所有拦截器
│ │ 全部 onContinue → 进入 2.4
│ │ 任一 onInterrupt → 终止,回调 NavigationCallback.onInterrupt()
│ │
│ ├── 2.4 _ARouter._navigation(postcard, requestCode, callback)
│ │ │
│ │ ├── 目标类型 = ACTIVITY
│ │ │ 构造 Intent:new Intent(context, postcard.getDestination())
│ │ │ 将 Postcard 的 Bundle 设置到 Intent:intent.putExtras(postcard.getExtras())
│ │ │ 设置 Flags:intent.setFlags(postcard.getFlags())
│ │ │ 在主线程执行 startActivity(intent)
│ │ │
│ │ ├── 目标类型 = FRAGMENT
│ │ │ 反射创建 Fragment 实例
│ │ │ 设置 arguments = postcard.getExtras()
│ │ │ 返回 Fragment 对象
│ │ │
│ │ └── 目标类型 = PROVIDER
│ │ 返回 postcard.getProvider()(在 completion 阶段已实例化)
│ │
│ └── 2.5 回调 NavigationCallback.onArrival()
│
▼
目标 Activity 启动 / Fragment 返回 / Provider 实例返回
4.2 关键源码:_navigation() 的分发逻辑
private Object _navigation(final Postcard postcard, final int requestCode,
final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
// ===== Activity 跳转 =====
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// 设置 Flags
int flags = postcard.getFlags();
if (flags != 0) {
intent.setFlags(flags);
}
// 如果不是 Activity Context,需要添加 NEW_TASK flag
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// 转场动画
String[] anim = postcard.getAnimations();
// 必须在主线程执行 startActivity
new Handler(Looper.getMainLooper()).post(() -> {
if (requestCode >= 0) {
// 需要结果回调
if (currentContext instanceof Activity) {
((Activity) currentContext)
.startActivityForResult(intent, requestCode);
}
} else {
currentContext.startActivity(intent);
}
// 应用转场动画
if (anim != null && currentContext instanceof Activity) {
((Activity) currentContext)
.overridePendingTransition(anim[0], anim[1]);
}
// 回调 onArrival
if (callback != null) {
callback.onArrival(postcard);
}
});
break;
case PROVIDER:
// ===== 返回 Provider 实例 =====
return postcard.getProvider();
case FRAGMENT:
// ===== 创建 Fragment 实例 =====
Class<?> fragmentClass = postcard.getDestination();
try {
Object instance = fragmentClass.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
// 创建 Fragment 失败
}
break;
default:
break;
}
return null;
}
4.3 拦截器的异步执行机制
拦截器的执行是异步且有超时保护的:
// InterceptorServiceImpl.java(ARouter 内置的拦截器服务)
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
@Override
public void doInterceptions(final Postcard postcard,
final InterceptorCallback callback) {
if (Warehouse.interceptors != null && Warehouse.interceptors.size() > 0) {
// 在线程池中执行拦截器链
LogisticsCenter.executor.execute(() -> {
// 使用 CancelableCountDownLatch 实现超时等待
CancelableCountDownLatch interceptorCounter =
new CancelableCountDownLatch(Warehouse.interceptors.size());
// _execute() 递归执行链式拦截器
_execute(0, interceptorCounter, postcard);
// 等待所有拦截器完成,最长等待 300 秒
interceptorCounter.await(
postcard.getTimeout(), TimeUnit.SECONDS
);
if (interceptorCounter.getCount() > 0) {
// 超时——有拦截器没有在规定时间内响应
callback.onInterrupt(
new HandlerException("拦截器处理超时")
);
} else if (exception == null) {
// 所有拦截器都通过了
callback.onContinue(postcard);
} else {
// 有拦截器主动中断了
callback.onInterrupt(exception);
}
});
} else {
// 没有拦截器,直接通过
callback.onContinue(postcard);
}
}
/**
* 递归执行拦截器链
* index = 0 时开始第一个拦截器
* 在 onContinue 的回调中递归调用 _execute(index + 1, ...)
*/
private static void _execute(final int index,
final CancelableCountDownLatch counter,
final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor interceptor = Warehouse.interceptors.get(index);
interceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// 当前拦截器通过,计数器 -1
counter.countDown();
// 执行下一个拦截器
_execute(index + 1, counter, postcard);
}
@Override
public void onInterrupt(Throwable exception) {
// 当前拦截器中断,取消等待
postcard.setTag(exception);
counter.cancel();
}
});
}
}
}
设计亮点:责任链模式 + CountDownLatch
拦截器链采用了经典的责任链模式,但与 OkHttp 不同的是,ARouter 的拦截器运行在子线程,因此使用 CountDownLatch 来同步等待结果。CancelableCountDownLatch 是 ARouter 自定义的——它在任何一个拦截器调用 onInterrupt 时立即将计数归零,使 await() 立刻返回,避免无谓等待。
五、 Postcard 与 RouteMeta:路由的数据模型
5.1 RouteMeta:路由元数据
RouteMeta 是路由信息的静态描述,在编译期生成、运行时加载:
public class RouteMeta {
private RouteType type; // ACTIVITY, FRAGMENT, PROVIDER 等
private Class<?> destination; // 目标类的 Class 对象
private String path; // 路由路径
private String group; // 分组名
private Map<String, Integer> paramsType; // 参数名 → 参数类型(用于 @Autowired)
private int priority; // 优先级(用于排序)
private int extra; // extras 标记位(用于拦截器判断)
private String name; // 路由的别名
}
5.2 Postcard:路由请求卡片
Postcard 继承自 RouteMeta,在其基础上增加了运行时的请求信息:
public final class Postcard extends RouteMeta {
// ===== 运行时附加信息 =====
private Bundle mBundle = new Bundle(); // 携带的参数数据
private int flags = 0; // Intent Flags
private int timeout = 300; // 拦截器超时时间(秒)
private IProvider provider; // Provider 实例(如果目标是 Provider)
private boolean greenChannel; // 是否走绿色通道
private Context context; // 发起跳转的 Context
private String action; // Intent Action
private int[] animations; // 转场动画资源 ID
// ===== Builder 方法 =====
public Postcard withString(String key, String value) {
mBundle.putString(key, value);
return this;
}
public Postcard withFlags(int flags) {
this.flags = flags;
return this;
}
public Postcard greenChannel() {
this.greenChannel = true;
return this;
}
// ...
}
RouteMeta 与 Postcard 的关系可以这样理解:
RouteMeta 是地图上的"地标信息"——固定不变的地址、类型、标签。Postcard 是一张"快递单"——除了地标信息外,还附带了这次发送的包裹(参数)、快递优先级、配送要求(Flags、动画)等运行时信息。
六、 arouter-register 插件:字节码插桩的性能优化
6.1 问题背景
在没有 arouter-register 插件时,ARouter.init() 必须扫描 APK 中的所有 Dex 文件来寻找路由映射类。这个过程涉及大量的文件 IO 和 ClassLoader 操作:
无插件时的初始化流程:
1. 获取 APK 中所有 Dex 文件的路径
2. 使用 DexFile API 遍历每个 Dex 中的所有类名
3. 对每个类名做字符串匹配:是否以 "com.alibaba.android.arouter.routes" 开头
4. 匹配到的类名缓存到 SharedPreferences
5. 对每个匹配到的类名执行 Class.forName() 加载
6. 实例化并调用 loadInto()
在大型 App 中(数万个类),步骤 2 可能需要 100-500ms,严重拖慢冷启动速度。
6.2 插件的工作原理:编译期注册
arouter-register 插件的核心思想是:在编译打包阶段就把"该加载哪些类"这条信息直接写死到代码里,完全消除运行时的 Dex 扫描。
它使用了 AGP(Android Gradle Plugin)的 Transform API 和 ASM 字节码操作库来实现:
Gradle 构建流程:
编译 Java/Kotlin → 生成 .class 文件 → Transform 阶段(插件在此介入)→ 打包 Dex
arouter-register 插件在 Transform 阶段的工作:
第一遍扫描:遍历所有 .class 文件
├── 找到实现了 IRouteRoot 的类 → 记录到列表 rootClassList
├── 找到实现了 IInterceptorGroup 的类 → 记录到列表
└── 找到实现了 IProviderGroup 的类 → 记录到列表
第二遍插桩:找到 LogisticsCenter.class
└── 使用 ASM 修改 loadRouterMap() 方法的字节码
在方法体中注入以下代码:
registerByPlugin = true;
register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_order");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_user");
register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$module_user");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$module_order");
6.3 ASM 插桩的具体代码
插件内部使用 ASM 的 MethodVisitor 来修改 loadRouterMap() 方法的字节码:
/**
* 使用 ASM 向 LogisticsCenter.loadRouterMap() 方法中注入注册代码
*/
class RouteMethodVisitor extends MethodVisitor {
@Override
void visitInsn(int opcode) {
// 在 RETURN 指令之前插入注册代码
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
// 插入:registerByPlugin = true
mv.visitInsn(Opcodes.ICONST_1);
mv.visitFieldInsn(Opcodes.PUTSTATIC,
"com/alibaba/android/arouter/core/LogisticsCenter",
"registerByPlugin", "Z");
// 对每个扫描到的路由类,插入 register("className") 调用
for (String className : scannedClassNames) {
mv.visitLdcInsn(className); // 将类名字符串压栈
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/alibaba/android/arouter/core/LogisticsCenter",
"register", "(Ljava/lang/String;)V", false);
}
}
super.visitInsn(opcode);
}
}
插桩前后的对比:
// ===== 插桩前(原始源码)=====
private static void loadRouterMap() {
registerByPlugin = false;
// 空方法体——什么都不做
}
// ===== 插桩后(被 ASM 修改后的字节码等价代码)=====
private static void loadRouterMap() {
registerByPlugin = true;
register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_order");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$module_user");
register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$module_user");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$module_order");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$module_user");
}
6.4 性能对比
| 指标 | 无插件(Dex 扫描) | 有插件(字节码插桩) |
|---|---|---|
| 初始化耗时 | 100-500ms(大型 App) | < 5ms |
| IO 操作 | 大量(遍历 Dex 文件) | 零 |
| ClassLoader 压力 | 高(逐个 Class.forName) | 低(只加载必要的类) |
| 加固兼容性 | ⚠️ 部分加固方案会破坏 Dex 结构 | ✅ 不依赖 Dex 结构 |
| AGP 版本限制 | 无 | ⚠️ Transform API 在 AGP 8.0+ 已废弃 |
6.5 AGP 版本兼容性问题
需要注意的是,arouter-register 插件依赖的 Transform API 在 AGP 7.4 后被标记为废弃,在 AGP 8.0 中被移除。如果你的项目升级到了 AGP 8.0+,需要:
- 使用适配了新版 AGP 的社区版本的注册插件
- 或者使用 ARouter 官方后续版本(如果有适配)
- 或者降级回 AGP 7.x
这也是 ARouter 在现代 Android 开发中面临的一个现实挑战——Gradle 构建生态的快速迭代可能导致基于 Transform API 的插件失效。
七、 Provider 的单例缓存机制
7.1 缓存策略
当通过 ARouter.getInstance().navigation(IUserService::class.java) 获取服务时,ARouter 保证同一个接口的实现类只会被实例化一次:
// LogisticsCenter.completion() 中的 Provider 处理逻辑
case PROVIDER:
Class<? extends IProvider> providerMeta =
(Class<? extends IProvider>) routeMeta.getDestination();
// 先从缓存中查找
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
// 缓存未命中——反射创建实例
IProvider provider = providerMeta.getConstructor().newInstance();
// 调用 init()——只在首次创建时调用一次
provider.init(mContext);
// 存入缓存
Warehouse.providers.put(providerMeta, provider);
instance = provider;
}
// 将实例绑定到 Postcard
postcard.setProvider(instance);
break;
设计考量:Provider 默认是单例的,这符合"服务"的语义——你不希望每次查找用户服务时都创建一个新的 UserServiceImpl 实例。init(Context) 方法只在首次创建时被调用,适合做一次性的初始化工作。
7.2 按类型查找的实现
navigation(Class<? extends T> service) 这个按类型查找的 API,其内部实现是在 Warehouse.providersIndex 中搜索:
protected <T> T navigation(Class<? extends T> service) {
// 在 providersIndex 中遍历,找到目标接口匹配的 RouteMeta
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
if (null == postcard) {
// 在 providersIndex 中找到匹配的 RouteMeta
// 然后构建 Postcard → completion() → 返回 Provider 实例
}
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
}
八、 线程安全设计
8.1 Warehouse 的并发控制
Warehouse 中的集合使用的是普通 HashMap,而非 ConcurrentHashMap。ARouter 的线程安全策略是:
初始化阶段(init):
LogisticsCenter.init() 方法标记为 synchronized
→ 初始化是串行的,不存在并发写入
运行阶段(navigation):
LogisticsCenter.completion() 方法标记为 synchronized
→ 按需加载分组时,通过 synchronized 保证多线程环境下的安全
读取阶段(routes.get()):
HashMap 的 get() 在只读场景下是线程安全的
因为初始化完成后,routes 只增不删(按需加载是追加操作)
这种设计在大方向上是合理的,但在极端并发场景下(多个线程同时首次访问同一个未加载的分组),synchronized 可能成为瓶颈。ARouter 选择了简单性优先——对于路由框架来说,completion() 的调用频率远低于网络请求等场景,锁竞争不是实际问题。
8.2 拦截器的线程模型
navigation() 调用
│
├── 主线程或任意线程
│
▼
InterceptorService.doInterceptions()
│
├── 将拦截器链提交到 LogisticsCenter.executor(线程池)
│
▼ 子线程
拦截器 1.process() → onContinue → 拦截器 2.process() → ... → 拦截器 N.process()
│
├── 全部通过:主线程 Handler.post() → startActivity()
└── 被拦截:回调 InterceptorCallback.onInterrupt()
注意:拦截器本身运行在子线程,但最终的 startActivity() 通过 Handler(Looper.getMainLooper()) 切回了主线程。这意味着拦截器内部可以安全地执行耗时操作(如网络请求验证 Token),不会阻塞 UI。
九、 核心设计模式总结
| 设计模式 | 应用位置 | 目的 |
|---|---|---|
| 门面模式 | ARouter → _ARouter |
隐藏内部实现复杂度,提供简洁的公共 API |
| Builder 模式 | Postcard 的链式 API |
灵活构建路由请求,支持可选参数 |
| 责任链模式 | InterceptorService 的拦截器链 |
按优先级依次处理路由请求,任一环节可中断 |
| 工厂方法 | RouteProcessor 生成 IRouteGroup |
将路由表的构建逻辑封装在工厂方法中 |
| 策略模式 | RouteType 驱动 _navigation() 的分支 |
根据路由类型(Activity/Fragment/Provider)选择不同的执行策略 |
| 单例模式 | Warehouse.providers 缓存 |
Provider 实例全局唯一 |
| 懒加载 | Group 的按需加载 | 只在首次访问时加载对应分组的路由表 |
| 代理模式 | $$ARouter$$Autowired 注入类 |
将参数解析逻辑代理给编译期生成的类 |
十、 架构全景:从编译到运行的完整数据流
┌──────────────────────── 编译期 ────────────────────────┐
│ │
│ @Route("/order/detail") @Interceptor(priority=8) │
│ @Autowired String orderId @Route("/user/service") │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ arouter-compiler (APT) │ │
│ │ │ │
│ │ RouteProcessor → ARouter$$Group$$order.java │ │
│ │ → ARouter$$Root$$module.java │ │
│ │ InterceptorProcessor → ARouter$$Interceptors$$ │ │
│ │ AutowiredProcessor → Xxx$$ARouter$$Autowired │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ arouter-register (Gradle 插件) │ │
│ │ │ │
│ │ Transform 扫描 → ASM 插桩 LogisticsCenter │ │
│ │ 注入 register("ARouter$$Root$$module_order") │ │
│ └─────────────────────────────────────────────────┘ │
│ │
├──────────────────────── 运行期 ────────────────────────┤
│ │
│ ARouter.init(application) │
│ │ │
│ ▼ │
│ LogisticsCenter.init() │
│ ├── loadRouterMap() → 插桩代码自动注册 │
│ ├── Root 类.loadInto(Warehouse.groupsIndex) │
│ ├── Interceptor 类.loadInto(Warehouse.interceptorsIndex) │
│ └── Provider 类.loadInto(Warehouse.providersIndex) │
│ │
│ ARouter.build("/order/detail").navigation() │
│ │ │
│ ├── build() → 创建 Postcard │
│ ├── completion() → 按需加载 Group → 补全 Postcard │
│ ├── 拦截器链 → 检查登录态等 │
│ └── _navigation() → Intent → startActivity() │
│ │
└───────────────────────────────────────────────────────┘
十一、 总结
ARouter 的源码架构展现了一个成熟的路由框架如何在编译期效率和运行时性能之间取得平衡:
-
编译期做"重活":APT 注解处理器在编译时完成路由表的生成、参数注入器的生成、拦截器索引的生成。这些"重活"只在编译时执行一次,不影响运行时性能。
-
打包期做"插桩":
arouter-register插件通过 ASM 字节码操作,将路由类的注册代码直接写入LogisticsCenter,消除了运行时 Dex 扫描的 IO 开销。 -
运行时做"查表":
Warehouse维护着精心组织的内存数据结构,LogisticsCenter通过按需加载和synchronized保证了线程安全和内存效率,InterceptorService通过责任链模式在子线程中异步执行拦截器链。 -
架构级别的解耦:门面模式(
ARouter/_ARouter)、策略模式(RouteType驱动分发)、责任链模式(拦截器)、懒加载模式(分组按需加载)——这些经典设计模式的组合运用,使得 ARouter 在功能丰富的同时保持了内部结构的整洁。
理解了这些核心机制,你不仅能在日常开发中更自信地使用 ARouter,也能在遇到路由异常时迅速定位问题——是路由表没有正确生成(检查 build/generated/source/kapt 目录下的生成代码),还是初始化有问题(检查 Warehouse.groupsIndex 是否为空),抑或是拦截器链出了意外(查看拦截器的 onContinue / onInterrupt 调用逻辑)。