注册

昨天我被开了,技术总监说:不会Arouter做什么架构师



ARouter,A framework for assisting in the renovation of Android componentization (帮助 Android App 进行组件化改造的路由框架) —— 支持模块间的路由、通信、解耦


官方中文介绍:
https://github.com/alibaba/ARouter/blob/master/README_CN.md
(中文比英文文档,详尽得多…,良心文档啊)


基本使用


1.添加依赖

android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
//注解处理器需要的模块名,作为路径映射的前缀
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}

dependencies {
implementation 'com.alibaba:arouter-api:1.4.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2' //注解处理器,会将注解编译成Java类
}

2.添加注解

//注意:路劲至少两级,即xx/xx,前一个xx用于分组
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {
}

3.初始化SDK

一般在Application中初始化


ARouter.init(this);

4.使用

//很简单,一句话完成,可携带参数
ARouter.getInstance().build("/test/second").navigation();

原理浅析


从ARouter.getInstance().build("/test/second").navigation();出发,解释其跳转基本过程。
先上一张时序图:
在这里插入图片描述


1.ARouter.build(path)构建Postcard

ARouter只是对外统一的api接口,实现基本由_ARouter完成,所以构建Postcard也是由_ARouter,构建,build(path, extractGroup(path))中extractGroup方法,就是把/xx/xx中前面的xx转换为默认group的方法,这也是之前必须使用2级以上目录的原因。build到此就完成了,此时还没有完成映射到activity的任务,只是把path浅析了下。


2.Postcard.navigation()实现跳转

最后也是由_ARouter完成,在_ARouter.navigation时,首先调用LogisticsCenter.completion(postcard)完善postcard的信息,而completion方法,则完成了path到activity的转换关系。完善后,再调用_navigation完成最终跳转。


3.LogisticsCenter.completion(postcard)将path映射到activity

核心部分:


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();
} else {
// Load route and cache it into memory, then delete from metas.
try {
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException();
}

completion(postcard); // Reload
}
} else {
postcard.setDestination(routeMeta.getDestination()); //destination就是需要跳转的Activity.class
......
}
}

首先去Warehouse的routes中寻找RouteMeta(路由元数据)。
Warehouse可以理解为存储路由元数据的容器,包括:路由关系、拦截器、provider的映射关系等。RouteMeta既持有activity等对应跳转类信息。
首次navigation时,RouteMeta == null,故会用postcard build时的group path先找到对一个的IRouteGroup信息[IRouteGroup何时加载到Warehouse的,见下条],然后通过iGroupInstance.loadInto将改分组下的RouteMeta都加载到缓存中,这可以理解为延迟加载,降低初始化时的一些压力。加载后,再重新调用completion(postcard)。


4.IRouteGroup的加载

路由元数据RouteMeta是从实现了IRouteGroup接口的实例中load进来的,这个实现了IRouteGroup接口的类在哪?何时load进Warehouse?在LogisticsCenter.init()方法中,找到了踪迹。


public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}

PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}

for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
......
}
}

首次init时,通过ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)将ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"下的所有类,都加载进routerMap中。然后通过区分routerMap类名,实例化com.alibaba.android.arouter.routes.ARouter##Root开头的类[由于csdn对$的支持并不友好,故以下$都使用#代替],调用其loadInto将对应的IRouteGroup类都加载到Warehouse.groupsIndex中。
PS:
init方法有一些小细节,针对debug版本或者新版本,才会有一次完整的类find和load的过程,加载完后会将类路径都存入sp,之后启动从sp拿,以增加启动速度。
ClassUtils.getFileNameByPackageName方法并不简单,其中牵扯了在多dex或者特殊rom下加载类的一些处理,有兴趣的同学可以阅读源码了解。


ARouter##Root##app


public class ARouter$$Root$$app implements IRouteRoot {
public ARouter$$Root$$app() {
}

public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("test", test.class);
}
}

ARouter##Group##test


public class ARouter$$Group$$test implements IRouteGroup {
public ARouter$$Group$$test() {
}

public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/test/second", "test", (Map)null, -1, -2147483648));
}
}

5.Route类的生成

接着上一步的解析,下面要了解的是,ARouter##Root##app,ARouter##Group##test这些编译好的类,是何时/如何生成的。这里使用的是android的apt(Annotation Processing Tool)技术,即在编译时,将注解生成为Java代码。所以在build.gradle添加依赖时,会添加注解处理器 annotationProcessor ‘com.alibaba:arouter-compiler:1.2.2’ ,具体的处理过程,可以参见arouter-compiler的RouteProcessor,RouteProcessor代码较长,这里就不详述了。感兴趣的童鞋,可以自己写一写注解处理器,com.squareup.javapoet神器了解一下。


资源下载:arouter-develop.zip

1 个评论

还好我会。

要回复文章请先登录注册