1、路由框架
1.1、原生路由的缺陷
原生路由方案一般是通过显式intent和隐式intent两种方式实现的,而在显式intent的情况下,因为会存在直接的类依赖的问题,导致耦合非常严重;而在隐式intent情况下,则会出现规则集中式管理,导致协作变得非常困难。而且一般而言配置规则都是在Manifest中的,这就导致了扩展性较差。除此之外,使用原生的路由方案会出现跳转过程无法控制的问题,因为一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会直接抛出运营级的异常。
1.2、路由组件
前面提到的主要是开发与协作中的问题,而使用一款路由框架时还会涉及到其他的两个大方面:一方面是组件化,而另一方面就是Native和H5的问题。刚才所提到的主要是开发和协作中作为开发者所需要面对的问题,而一旦一款APP达到一定体量的时候,业务就会膨胀得比较严重,而开发团队的规模也会越来越大,这时候一般都会提出组件化的概念。组件化就是将APP按照一定的功能和业务拆分成多个小组件,不同的组件由不同的开发小组来负责,这样就可以解决大型APP开发过程中的开发与协作的问题,将这些问题分散到小的APP中。目前而言组件化已经有非常多比较成熟的方案了,而自定义路由框架也可以非常好地解决整个APP完成组件化之后模块之间没有耦合的问题,因为没有耦合时使用原生的路由方案肯定是不可以的。
1.3、路由组件的特点
分发:把一个URL或者请求按照一定的规则分配给一个服务或者页面来处理,这个流程就是分发,分发是路由框架最基本的功能,当然也可以理解成为简单的跳转。
管理:将组件和页面按照一定的规则管理起来,在分发的时候提供搜索、加载、修改等操作,这部分就是管理,也是路由框架的基础,上层功能都是建立在管理之上。
控制:就像路由器一样,路由的过程中,会有限速、屏蔽等一些控制操作,路由框架也需要在路由的过程中,对路由操作做一些定制性的扩展,比方刚才提到的AOP,后期的功能更新,也是围绕这个部分来做的。
2、ARouter的技术方案
1、最基础的Compiler这个SDK,其内部有三个处理器,分别是:Route Processor,Interceptor Processor以及Autowire Processor,通过名字就可以看出这三个处理器分别是处理路径路由、拦截器和进行自动装配的。
2、在编译过程中,还有一个gradle插件,目的是在Arouter中插入相关注册代码代替在Arouter.Init()时扫描dex文件获取到所有route相关类。
3、API的SDK是用户在运行期使用的,这一部分主要分为四层:
Launcher:这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
facade:从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template,这里的Service概念和服务端的Service概念是相似的,也是在客户端的简单引申,但是却不同于Android组件中的Service,这里的Service是ARouter抽象出来的概念,从本质上讲,这里的Service是接口,从意义上讲是将一定的功能和组件封装成接口,并对外提供能力。Template则是模板,主要用于在编译期执行的SDK,这个SDK会在编译期生成一些映射文件,而这些映射文件会按照Template组件中提供的模板来生成,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。
SDK内部实现:这一层包括了Ware House、Thread、Log、Exception以及Class工具。Ware House主要存储了ARouter在运行期间加载的一些配置文件以及映射关系;而Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的;Class工具则是用于解决不同类型APK的兼容问题的。
Logistics Center:从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层,当然也会按照功能模块进行划分。
3、源码分析
主要代码结构
module | 功能 |
---|---|
arouter-api | 上层主要代码,包括入口类ARouter,主要逻辑代码类LogisticsCenter,相关辅助类ClassUtils等 |
arouter-annotation | ARouter中主要支持的annotation(包括Autowired, Route, Interceptor)的定义,以及RouteMeta等基础model bean的定义 |
arouter-compiler | ARouter中annotation对应的annotation processor代码(注解处理器:让这些注解代码起作用,Arouter中主要是生成相关代码 |
arouter-gradle-plugin | 一个gradle插件,目的是在arouter中插入相关注册代码(代替在Init时扫描dex文件获取到所有route相关类) |
module-java | 示例 |
module-java-export | 示例 |
module-kotlin | 示例 |
App | 示例 |
3.1、arouter-annotation
@Route路由注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path(); // Path of route
String group() default ""; //Used to merger routes, the group name MUST BE USE THE COMMON WORDS
String name() default ""; //Name of route, used to generate javadoc.
int extras() default Integer.MIN_VALUE; //Extra data, can be set by user.
int priority() default -1; //The priority of route
}
@Interceptor拦截器注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
int priority(); //The priority of interceptor, ARouter will be excute them follow the priority.
String name() default "Default"; //The name of interceptor, may be used to generate javadoc.
}
@Autowired自动装载注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
String name() default ""; //Mark param's name or service name.
boolean required() default false; // If required, app will be crash when value is null.Primitive type wont be check!
String desc() default ""; //Description of the field
}
RouteType路由类型
public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
int id;
String className;
...
}
TypeKind参数类型
public enum TypeKind { //目标Class的需要注入的参数的参数类型
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
SERIALIZABLE,
PARCELABLE,
OBJECT;
}
RouteMeta路由元信息
public class RouteMeta { //每一个RouteMeta就是一条路由元信息
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
private String name;
private Map<String, Autowired> injectConfig; // Cache inject config.
public RouteMeta() {
}
}
用于保存一条路由信息。
3.2、arouter-compiler注解处理器
ARouter注解处理器一共三种:分别为AutowiredProcessor、InterceptorPorcessor和RouteProcessor,对应处理 @Autowired注解、@Interceptor注解和@Router注解
由注解处理器处理之后,生成的代码主要如下:其中Autowired由注解@Autowired和注解处理器AutowiredProcessor生成,Interceptor由注解@Inpterceptor和InterceptorProcessor生成,Group、Provider、Route均有注解@Route和RouteProcessor生成。
BaseProcessor:注解处理器的基类
RouteProcessor:路由注解处理器,生成Group、Provider、Root三种类型的代码
AutowiredProcessor:自动装载注解处理器,生成Autowired类型的代码
InterceptorProcessor:拦截器注解处理器,生成Interceptor类型的代码
3.3、arouter-gradle-plugin 插件
ARouter插件在编译过程中,完成代码的插入,以便于业务方在调用ARouter.init()的时候直接获取到被@Route注解的相关的类信息,替代业务方在ARouter.init()时通过扫描dex文件来获取@Route注解的相关的类的信息。
com.alibaba.aouter.properties
//properties里就一行内容,插件入口为PluginLaunch
implementation-class=com.alibaba.android.arouter.register.launch.PluginLaunch
ScanSetting:需要扫描的基础信息
PluginLaunch:插件的入口
RegisterTransform:插件具体执行逻辑的地方
3.4、arouter-api
WareHouse:数据存储的仓库
/**
* Storage of route meta and other data.
*/
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); //init 初始化之后扫出来的东西放在这里
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
3、ARouter使用流程分析
Apk的打包编译流程
如上图所示,我们可以在步骤1和步骤2处对代码进行改造:
1、在步骤1处,由.java文件生成.class文件时,APT、AndroidAnnotation等就是在此处触发代码生成。 2、在步骤2处,有.class文件进一步优化生成.dex文件时,也就是直接操作字节码文件,就是字节码插桩。
路由注解部分
在分享路由注解部分时,会讲到两部分的内容,一部分是跟注解处理器相关的代码,一部分是跟代码插入相关的代码
路由注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path();//Path of route
String group() default "";//Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!
String name() default "";//Name of route, used to generate javadoc.
int extras() default Integer.MIN_VALUE;//Extra data, can be set by user.
int priority() default -1;//The priority of route.
}
路由注解处理器
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map<String, String> rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);//拿到被Route注解了的Element
try {
logger.info(">>> Found routes, start... <<<");
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
//将上面拿到的Elment传给parseRoutes处理
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// prepare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*
构建 $$Rout$$ loadInto()的形参类型
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
构建 $$Group$$ 和 $$provider$$ loadInto()的形参类型
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
//构建 $$Rout$$ loadInto()的形参Map<String, Class<? extends IRouteGroup>> routers
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
//构建 $$Group$$ loadInto()的形参Map<String, RouteMeta> atlas
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
//构建 $$Providers$$ loadInto()的形参Map<String, RouteMeta> providers
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
//构建方法 loadInto():
//@Override
//public void loadInto(Map<String, Class<? extends IRouteGroup>> routers)
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
//遍历所有被@Route注解的element
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
// Activity or Fragment
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
//对路由节点进行分组,按照GroupName作为key,对应的GroupName相同的RouteMeta构成的Set作为value,数据放在groupMap里面
categories(routeMeta);
}
//构建方法 ARouter$$Providers$$app loadInto():
//@Override
//public void loadInto(Map<String, RouteMeta> providers)
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
//构建方法 ARouter$$Group$$ loadInto():
//@Override
//public void loadInto(Map<String, RouteMeta> atlas)
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// Build group method body
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
RouteDoc routeDoc = extractDocInfo(routeMeta);
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
//针对provider有单独的处理,loadIntoMethodOfProviderBuilder添加了对应的代码块
switch (routeMeta.getType()) {
case PROVIDER: // Need cache provider's super class
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
// Make map body for paramsType
StringBuilder mapBodyBuilder = new StringBuilder();
Map<String, Integer> paramsType = routeMeta.getParamsType();
Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
if (MapUtils.isNotEmpty(paramsType)) {
List<RouteDoc.Param> paramList = new ArrayList<>();
for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
mapBodyBuilder.append("put("").append(types.getKey()).append("", ").append(types.getValue()).append("); ");
RouteDoc.Param param = new RouteDoc.Param();
Autowired injectConfig = injectConfigs.get(types.getKey());
param.setKey(types.getKey());
param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
param.setDescription(injectConfig.desc());
param.setRequired(injectConfig.required());
paramList.add(param);
}
routeDoc.setParams(paramList);
}
String mapBody = mapBodyBuilder.toString();
//$$Group$$ 的loadInto里,添加对应的代码(在内层的routeMeta循环里)
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
}
// 按照GroupName的形式生成对应的ARouter$$Group$$groupName的文件(在外层的entry循环里),Group文件会有很多个
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
//将groupName和生成的GroupFileName存起来,用于生成ARouter$$Root$$xxx文件
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
//在rootMap有数据的情况下,往loadIntoMethodOfRootBuilder也就是Arouter$$Root$$Name的loadInto里添加代码
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// Output route doc
if (generateDoc) {
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// 生成ARouter$$Providers$$moduleName文件
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");
// 生成ARouter$$Root$$moduleName文件
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}
RouteProcess流程里面,一共做了以下几件事:
1、生成ARouter$$Group$$GroupName文件,生成ARouter$$Provider$$ModuleName文件,生成ARouter$$Root$$ModuleName文件
2、对于Provider类型的,同时存在与Group和Provider文件里,但是存储的形式不同
3、Root里面存放的是Group信息,Group里面存放的才是RouteMeta信息
生成的Group类示例代码
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
public class ARouter$$Group$$yourservicegroupname implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/yourservicegroupname/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
atlas.put("/yourservicegroupname/json", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
atlas.put("/yourservicegroupname/single", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
生成的Provider类示例代码
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
生成的Root类示例代码
public class ARouter$$Root$$modulejava implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("m2", ARouter$$Group$$m2.class);
routes.put("module", ARouter$$Group$$module.class);
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}
代码插装部分
ScanSetting:扫描信息
class ScanSetting {
static final String PLUGIN_NAME = "com.alibaba.arouter"
/**
* The register code is generated into this class
*/
static final String GENERATE_TO_CLASS_NAME = 'com/alibaba/android/arouter/core/LogisticsCenter'
static final String GENERATE_TO_CLASS_FILE_NAME = GENERATE_TO_CLASS_NAME + '.class' //需要插入代码的类
static final String GENERATE_TO_METHOD_NAME = 'loadRouterMap'//需要被插入代码的方法
static final String ROUTER_CLASS_PACKAGE_NAME = 'com/alibaba/android/arouter/routes/'
private static final INTERFACE_PACKAGE_NAME = 'com/alibaba/android/arouter/facade/template/'
static final String REGISTER_METHOD_NAME = 'register'//需要插入代码的方法
String interfaceName = ''
File fileContainsInitClass
ArrayList<String> classList = new ArrayList<>() //存放扫出来的类的list
ScanSetting(String interfaceName){
this.interfaceName = INTERFACE_PACKAGE_NAME + interfaceName
}
}
PluginLaunch:插入件的入口
PluginLaunch.groovy
public class PluginLaunch implements Plugin<Project> {
@Override
public void apply(Project project) {
def isApp = project.plugins.hasPlugin(AppPlugin)
//only application module needs this plugin to generate register code
if (isApp) {
Logger.make(project)
Logger.i('Project enable arouter-register plugin')
def android = project.extensions.getByType(AppExtension)
def transformImpl = new RegisterTransform(project)
//init arouter-auto-register settings
ArrayList<ScanSetting> list = new ArrayList<>(3)
list.add(new ScanSetting('IRouteRoot'))
list.add(new ScanSetting('IInterceptorGroup'))
list.add(new ScanSetting('IProviderGroup'))
RegisterTransform.registerList = list
//register this plugin
android.registerTransform(transformImpl)
}
}
}
1、PluginLaunch里面初始化了一个list,里面放入了三个接口 IRouteRoot、IIterceptorGroup、 IproviderGroup 类型,后续会扫出这三个接口的实现类,在前面的注解处理器里被@Route注解生成的三种类型的代码以及后面的被@Interceptor注解生成的代码,就是继承自这三种类型。
2、注册了一个Transform,代码的实际插入在RegisterTransform里面。
Google官方在Android GradleV1.5.0版本以后提供了Transform API,它允许第三方插件在编译后的类文件转换为dex文件之前做处理操作,我们需要做的就是实现Transform来对.class文件便遍历来拿到所有方法,修改完成后再对源文件进行替换就可以了。
/**
* transform api
* <p>
* 1. Scan all classes to find which classes implement the specified interface
* 2. Generate register code into class file: {@link ScanSetting#GENERATE_TO_CLASS_FILE_NAME}
* @author billy.qi email: qiyilike@163.com
* @since 17/3/21 11:48
*/
class RegisterTransform extends Transform {
Project project
static ArrayList<ScanSetting> registerList
static File fileContainsInitClass;
RegisterTransform(Project project) {
this.project = project
}
// 返回对应的 Transform Task的名称。
@Override
String getName() {
return ScanSetting.PLUGIN_NAME
}
// 输入文件的类型:可供我们去处理的有两种类型, 分别是编译后的java代码, 以及资源文件(非res下文件, 而是assests内的资源)
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_CLASS
}
// 指定插件的试用范围:这个插件会扫描工程里所有的Classes
@Override
Set<QualifiedContent.Scope> getScopes() {
return TransformManager.SCOPE_FULL_PROJECT
}
// 是否支持增量:如果支持增量执行, 则变化输入内容可能包含 修改/删除/添加 文件的列表
@Override
boolean isIncremental() {
return false
}
// Transform的执行主函数,进行具体的转换过程
@Override
void transform(Context context, Collection<TransformInput> inputs
, Collection<TransformInput> referencedInputs
, TransformOutputProvider outputProvider
, boolean isIncremental) throws IOException, TransformException, InterruptedException {
Logger.i('Start scan register info in jar file.')
long startTime = System.currentTimeMillis()
boolean leftSlash = File.separator == '/'
if (!isIncremental){
outputProvider.deleteAll()
}
inputs.each { TransformInput input ->
//通过AMS 的 ClassVisistor 扫描所有的jar 文件,将所有扫描到的IRouteRoot IInterceptorGroup IInterceptorGroup类都加到ScanSetting 的 classList中
//如果jar包是 LogisticsCenter.class,标记该类文件到 fileContainsInitClass
// scan all jars
input.jarInputs.each { JarInput jarInput ->
String destName = jarInput.name
// rename jar files
def hexName = DigestUtils.md5Hex(jarInput.file.absolutePath)
if (destName.endsWith(".jar")) {
destName = destName.substring(0, destName.length() - 4)
}
// input file
File src = jarInput.file
// output file
File dest = outputProvider.getContentLocation(destName + "_" + hexName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
//scan jar file to find classes
if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
ScanUtil.scanJar(src, dest)
}
FileUtils.copyFile(src, dest)
}
// scan class files
input.directoryInputs.each { DirectoryInput directoryInput ->
File dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
String root = directoryInput.file.absolutePath
if (!root.endsWith(File.separator))
root += File.separator
directoryInput.file.eachFileRecurse { File file ->
def path = file.absolutePath.replace(root, '')
if (!leftSlash) {
path = path.replaceAll("\\", "/")
}
if(file.isFile() && ScanUtil.shouldProcessClass(path)){
ScanUtil.scanClass(file)
}
}
// copy to dest
FileUtils.copyDirectory(directoryInput.file, dest)
}
}
Logger.i('Scan finish, current cost time ' + (System.currentTimeMillis() - startTime) + "ms")
if (fileContainsInitClass) {
registerList.each { ext ->
Logger.i('Insert register code to file ' + fileContainsInitClass.absolutePath)
if (ext.classList.isEmpty()) {
Logger.e("No class implements found for interface:" + ext.interfaceName)
} else {
ext.classList.each {
Logger.i(it)
}
RegisterCodeGenerator.insertInitCodeTo(ext)
}
}
}
Logger.i("Generate code finish, current cost time: " + (System.currentTimeMillis() - startTime) + "ms")
}
}
ScanUtil.groovy
static void scanJar(File jarFile, File destFile) {
if (jarFile) {
def file = new JarFile(jarFile)
Enumeration enumeration = file.entries()
while (enumeration.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) enumeration.nextElement()
String entryName = jarEntry.getName()
//com/alibaba/android/arouter/routes/xxx是否有这种路径的文件
if (entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)) {
InputStream inputStream = file.getInputStream(jarEntry)
scanClass(inputStream)
inputStream.close()
} else if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {
//是否有com/alibaba/android/arouter/core/LogisticsCenter.java文件
// mark this jar file contains LogisticsCenter.class
// After the scan is complete, we will generate register code into this file
RegisterTransform.fileContainsInitClass = destFile
}
}
file.close()
}
}
1、通过ASM扫描了对应的jar 文件和class文件,并将扫描到的对应routes包下的类加入到ScanSetting 的classList属性 中
2、如果扫描到包含LogisticsCenter.class 类文件,将该文件记录到fileContainsInitClass 字段中。
3、扫描完成的文件重命名。
4、最后通过RegisterCodeGenerator.insertInitCodeTo(ext) 方法插入初始化代码到LogisticsCenter.class中。
在整个编译的过程中,一共完成了两件事情:
1、生成被@Route、@Interceptors、@Autewired注解的类的相关的辅助类
2、往LogisticsCenter.loadRouterMap()方法里插入第一步生成的类的信息。
ARouter初始化
在ARouter的init()流程中,一共完成了两件时间:
1、Warehouse.groupsIndex,Warehouse.providersIndex,Warehouse.interceptorsIndex被放入了数据
2、初始化了一个被@Route(path = "/arouter/service/interceptor")注解的InterceptorServiceImpl,并调用了其init()。
ARouter.java
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit(); //拦截器的初始化调用
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
_ARouter.java
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
//在afterInit里面,调用了InterceptorService
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context; //静态持有的Application上下文
executor = tpe; //静态持有的线程池
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap(); //这个方法把registerByPlugin设置为false,做了优化
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { //如果是Debug版本或者新版本,会直接重新加载所有Router
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler. 通过指定包名com.alibaba.android.arouter.routes找到编译期产生的routers目录下的类名(不包含装载类)
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 { //如果不是debug版本和新版本,则直接从Sp里取出routerMap
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
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);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
在LogisticsCenter.init()函数里面:首次调用时,会你用ClassUtils.getFileNameByPackageName()来扫描对应的包下面的所有的className,在首次扫描完成之后会存储在Sp里面,这样后面不需要再次扫描。
ClassUtils.java
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的 集合
*/
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
启动优化
LogisticsCenter.java
//arouter-auto-register 插件将在此方法中生成代码调用此方法来注册所有路由器、拦截器和提供程序
/**
* arouter-auto-register plugin will generate code inside this method
* call this method to register all Routers, Interceptors and Providers
*/
private static void loadRouterMap() {
registerByPlugin = false;
// auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}
loadRouterMap()函数里利用了字节码插桩来优化ARouter的首次启动耗时,在初始化时,会在扫描之前,判断registerByPlugin,如果我们需要的map已经被插件注册了,那也就不需要进行下面的耗时操作了但是我们可以看到在loadRouterMap中,registerByPlugin一直被设为false
LogisticsCenter.java
//源码代码,插桩前
private static void loadRouterMap() {
//registerByPlugin一直被置为false
registerByPlugin = false;
}
//插桩后反编译代码
private static void loadRouterMap() {
registerByPlugin = false;
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
}
//字节码形式,可以看到一共插入了7个register函数
.method private static loadRouterMap()V
.registers 2
.line 64
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "loadRouterMap"
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 65
const/4 v0, 0x0
sput-boolean v0, Lcom/alibaba/android/arouter/core/LogisticsCenter;->registerByPlugin:Z
.line 71
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi"
invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V
return-void
.end method
LogisticsCenter.java
/**
* register by class name
* Sacrificing a bit of efficiency to solve
* the problem that the main dex file size is too large
*/
private static void register(String className) {
logger.info(TAG, "register before use");
if (!TextUtils.isEmpty(className)) {
try {
Class<?> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
registerInterceptor((IInterceptorGroup) obj);
} else {
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}
} catch (Exception e) {
logger.error(TAG,"register class error:" + className, e);
}
}
}
/**
* method for arouter-auto-register plugin to register Routers
* @param routeRoot IRouteRoot implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerRouteRoot(IRouteRoot routeRoot) {
markRegisteredByPlugin();
if (routeRoot != null) {
routeRoot.loadInto(Warehouse.groupsIndex);
}
}
/**
* method for arouter-auto-register plugin to register Providers
* @param providerGroup IProviderGroup implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerProvider(IProviderGroup providerGroup) {
markRegisteredByPlugin();
if (providerGroup != null) {
providerGroup.loadInto(Warehouse.providersIndex);
}
}
/**
* method for arouter-auto-register plugin to register Interceptors
* @param interceptorGroup IInterceptorGroup implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
markRegisteredByPlugin();
if (interceptorGroup != null) {
interceptorGroup.loadInto(Warehouse.interceptorsIndex);
}
}
/**
* mark already registered by arouter-auto-register plugin
*/
private static void markRegisteredByPlugin() {
if (!registerByPlugin) {
registerByPlugin = true;
}
}
初始化工作全部完成,其中仓库Warehouse缓存了全局应用的信息,找到三种对应的文件,然后调用其文件下的loadInto方法,把数据添加到下面几个xxx_Index变量中:
Warehouse.groupsIndex:存放的Root信息,每个Root信息里面存放的是当前module下的Group信息,Group下的信息才是每一个被@Route注解的RouteMeta信息
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
Warehouse.interceptorsIndex:存放的拦截器的信息
register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
Warehouse.providersIndex:存放的provider的信息
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
ARouter调用流程
ARouter.getInstance()
.build("/test/activity2")
.navigation();
ARouter.java
public static ARouter getInstance() {
if (!hasInit) { //如果没有初始化过,则直接抛出异常,所以使用ARouter之后,需要调用ARouter.init()
throw new InitException("ARouter::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
}
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
_ARouter.java
/**
* Build postcard by path and default group
*/
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true); //传进来的path,根据path生产的Group,三个参数分别是 /test/activity2, test, true
}
}
//根据path拿到group
private String extractGroup(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1)); ///拿到传入的path /test/activity2 /前的内容test作为group
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
//new一个PostCard出来,这里有一个PathReplaceService是个接口,没有实现,业务方可以根据时候需要对path进行二次处理来决定是否需要实现一个PathReplaceService,但是在这个地方没用
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) { //afterReplace == true,所以不走这个逻辑
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
public interface PathReplaceService extends IProvider {
String forString(String path);
Uri forUri(Uri uri);
}
ARouter.getInstance().build("/test/activity2"),在build完成之后,拿到了一个由参数 /test/activity2 和 test 实例化的PostCard
PostCard.java
public Postcard(String path, String group) {
this(path, group, null, null);
}
//开始navigation
public Object navigation() {
return navigation(null);
}
//继续navigation
public Object navigation(Context context) {
return navigation(context, null);
}
//继续navigation
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback); //context是null, this是new出来的PostCard,requestCode是-1,callback是null
}
Postcard里面的navigation最终还是调用到_ARouter里面的navigation,传入的context和callback均为null。
ARouter.java
/**
* Launch the navigation.
*
* @param mContext null
* @param postcard 实例化的对象
* @param requestCode Set for startActivityForResult -1
* @param callback null
*/
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
_Aouter.java
/**
* Use router navigation.
*
* @param context Activity or null.
* @param postcard Route metas
* @param requestCode RequestCode
* @param callback cb
*/
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context); //传进来的context为null,mContext为ARouter.init的时候传入的Application
try {
LogisticsCenter.completion(postcard); //补全postCard,现在的postCard里面只有path 和 group
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//补全后的postCard执行下面的逻辑,call为null
if (null != callback) {
callback.onFound(postcard);
}
//是否是绿色通道,及是否需要进行拦截器部分的操作,拦截器部分执行完成之后的回调为onContinue,回调里执行_navigation,如果是绿色通道,及不需要执行拦截器操作的时候,直接执行_navtigation
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(postcard, requestCode, callback);
}
return null;
}
在_ARouter.navigation的LogisticsCenter.completion(postcard) 对Postcard进行补全。
LogisticsCenter.java
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
*/
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { //首次进来是Warehouse.routers里面为空,获取到的routeMete为null
// Maybe its does't exist, or didn't load.
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) { //init里面的groupsIdex里面没有包含要跳转的postCard里面的group直接抛出异常
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
addRouteGroupDynamic(postcard.getGroup(), null); //将这个postCard的group添加到WareHouse.
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // 重新调用此方法
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri(); //示例这里的uri为null,type为Activity,下面的代码逻辑都不会走
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
//动态加载Group里面的内容
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//根据GroupName从Warehouse.groupIndex里面,拿到Group的类名
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);//将信息添加到Warehouse.routes里面
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
上诉代码以/test/activity2为例,在addRouteGroupDynamic()里面,我们传入的groupName为test。
1、通过test在Warehouse.groupIndex.get(test)拿到这个通过注解生成的类
2、例化之后调用loadInfo方法,把Warehouse.routes传入,相当于往Warehouse.routes里面添加数据
3、完成之后Warehouse.routes里面的数据如下,添加数据完成之后,再次调用completion函数,再从Warehose.routes里获取RouteMeta
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());//根据path拿到对应的RouteMeta
RouteMeta{type=ACTIVITY, rawType=null, destination=class com.alibaba.android.arouter.demo.module1.testactivity.Test2Activity, path='/test/activity2', group='test', priority=-1, extra=-2147483648, paramsType={key1=8}, name='null'}
4、重新调用completion(postcard)
5、执行_navigation()
_Aouter.java
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class<?> fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
if (requestCode >= 0) { // Need start for result
if (currentContext instanceof Activity) {
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
logger.warning(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
}
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over
callback.onArrival(postcard);
}
}
拦截器部分
拦截器注册
拦截器的使用和@Route的使用差不多,只是试用了另一个注解@Interceptor,有两个变量,priority是拦截器的优先级,值越小优先级越高,会优先拦截,name是拦截器的名称。
拦截器的注解
Interceptor.java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}
拦截器的例子:拦截器都实现了Interceptor的process接口,在process函数里对传入的postcard进行拦截操作。
@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
}
@Override
public void init(Context context) {
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}
@Interceptor(priority = 90)
public class TestInterceptor90 implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
callback.onContinue(postcard);
}
@Override
public void init(Context context) {
Log.e("test", "位于moudle1中的拦截器初始化了");
}
}
public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
拦截器因为是自动注册的,所以可以将不同功能的拦截器放在不同功能的模块中,只有模块被打包到整个项目中,因为自动注册机制所以拦截器就会生效,如果不将这些拦截器放到模块并打包到项目中,那就不会生效,这样就不用去做很多注册与反注册的工作,这也是ARouter适用于模块开发的原因之一。
拦截器的注解处理器
@AutoService(Processor.class)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends BaseProcessor {
private Map<Integer, Element> interceptors = new TreeMap<>();
private TypeMirror iInterceptor = null;
//初始化,做一些你想做的操作
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
iInterceptor = elementUtils.getTypeElement(Consts.IINTERCEPTOR).asType();
}
//process部分,扫描被@Interceptor注解的类,生成代码
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
try {
parseInterceptors(elements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
//扫出来的element交给parseInterceptors处理
private void parseInterceptors(Set<? extends Element> elements) throws IOException {
if (CollectionUtils.isNotEmpty(elements)) {
// 校验和缓存数据
for (Element element : elements) {
if (verify(element)) { // Check the interceptor meta
logger.info("A interceptor verify over, its " + element.asType());
Interceptor interceptor = element.getAnnotation(Interceptor.class);
//查看是否有相同优先级的拦截器,如果有,抛出异常
Element lastInterceptor = interceptors.get(interceptor.priority());
if (null != lastInterceptor) { // Added, throw exceptions
throw new IllegalArgumentException(
String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
interceptor.priority(),
lastInterceptor.getSimpleName(),
element.getSimpleName())
);
}
//缓存对应的优先级和element
interceptors.put(interceptor.priority(), element);
} else {
logger.error("A interceptor verify failed, its " + element.asType());
}
}
// Interface of ARouter.
TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);
TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);
/**
* 生成形参的类型
* ```Map<Integer, Class<? extends IInterceptor>>```
*/
ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))
)
);
/**
* 生成的形参
* ```Map<Integer, Class<? extends IInterceptor>> interceptors```
*/
ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();
/**
* 生成的方法:loadInto
*/
MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(tollgateParamSpec);
// 遍历interceptors里面的数据生成loadInto里面的代码,key和value对应的就是优先级和被注解的类
if (null != interceptors && interceptors.size() > 0) {
// Build method body
for (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {
loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
}
}
// 生成对应的类
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
.addModifiers(PUBLIC)
.addJavadoc(WARNING_TIPS)
.addMethod(loadIntoMethodOfTollgateBuilder.build())
.addSuperinterface(ClassName.get(type_IInterceptorGroup))
.build()
).build().writeTo(mFiler);
logger.info(">>> Interceptor group write over. <<<");
}
}
/**
* Verify inteceptor meta
*
* @param element Interceptor taw type
* @return verify result
*/
private boolean verify(Element element) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
// It must be implement the interface IInterceptor and marked with annotation Interceptor.
return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);
}
}
生成的代码如下
public class ARouter$$Interceptors$$modulejava implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(7, Test1Interceptor.class);
interceptors.put(90, TestInterceptor90.class);
}
}
拦截器初始化
拦截器的初始化,在Arouter.init的时候实现,init函数里除了上文分析的_ARouter.init()部分的内容,在init结束之后,还有一个_Arouter.afterInit()函数的调用。
Arouter.java
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
_Arouter.java
private static InterceptorService interceptorService;//初始化了一个interceptorSerive,后续的拦截器的操作,都是通过这个service实现的
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
在afterInit函数里面,启动了一个被@Route注解的Service,实现了InterceptorService的doInterceptions()接口。
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {}
public interface InterceptorService extends IProvider {
//拦截操作的接口
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
public interface IProvider {
void init(Context context);
}
public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
//因为上面见过这个navigation了,所以这里简化一下,关注我们需要的部分
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
LogisticsCenter.completion(postcard); //补全这个postCard
} catch (NoRouteFoundException ex) {
}
Log.e(Consts.TAG, "navigation postcard after completion : " + postcard.toString());
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) {
} else {
Log.e(Consts.TAG, "navigation postcard.isGreenChannel()");
return _navigation(postcard, requestCode, callback);
}
return null;
}
//这次的completion里面,如果类型为provider会有特殊的操作
public synchronized static void completion(Postcard postcard) {
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
addRouteGroupDynamic(postcard.getGroup(), null);
completion(postcard)
} else {
Log.e(Consts.TAG, "completion postcard routeMeta : " + routeMeta.toString());
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
//针对如果是Provider类型,1、调用init函数 2、Warehouse.provider里面存入这个provider 3、postcard里面setProvider 4、设置greenChannel
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
Log.e(Consts.TAG, "Aroute init provider : " + provider.getClass().getName());
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
default:
break;
}
}
}
//返回在completion 里面setProvider的值给interceptorService,这个provider,也就是实例化了一个
InterceptorServiceImpl赋值给interceptorService
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
System.out.println("_navigation getType : " + postcard.getType());
switch (postcard.getType()) {
case ACTIVITY:
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
一顿navigation操作,补全代码操作之后,找到了在ARouter$$Group$$arouter里面的/arouter/service/interceptor的对应的RouteMeta信息,completion里调用了provider的init()及调用了InterceptorServiceImpl的init()函数
public class ARouter$$Group$$arouter implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/arouter/service/autowired", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", null, -1, -2147483648));
atlas.put("/arouter/service/interceptor", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", null, -1, -2147483648));
}
}
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
}
//遍历被注册到Warehouse.interceptorsIndex里面的拦截器,调用被遍历到的拦截器的init函数,并把对应的拦截器添加到Warehouse.interceptors里界面,再把标志位interceptorHasInit设置为true
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);//拦截器被取出来放到了interceptors里面
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
}
到这里为止,在afterInit()里面的,关于拦截器的初始化就执行完毕了,并没有直接的调用被注册的拦截器的init函数,而是通过了一个被@Route注解的InterceptorServiceImpl来间接调用到的。
拦截器的拦截操作
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
System.out.println("navigation" );
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {}
//一般情况下,postCard是没有设置GreenChannel的,所以他们都会经过拦截器的拦截,这个interceptorService就是在afterInit里面被赋值的变量,也就是InterceptorServiceImpl
//所以这个地方调用了InterceptorServiceImpl.doInterceptions函数
if (!postcard.isGreenChannel()) {
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
});
} else {
return _navigation(postcard, requestCode, callback);
}
return null;
}
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus(); //检查是否完成了初始化
if (!interceptorHasInit) { //如果没有完成初始化,则会直接回调中断拦截操作
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size()); //计数、超时
try {
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);//300S超时
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
callback.onInterrupt((Throwable) postcard.getTag());
} else {
callback.onContinue(postcard); //拦截器执行完毕之后,在_Arouter里面,执行continue回调,继续完成后面的_navigation操作
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
//这里的这个callback是_ARouter里面的传进来的callback
callback.onContinue(postcard);
}
}
//从Warehouse.interceptors里面有序的取出拦截器,执行拦截操作
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() { //取出来的拦截器,对这个postCard执行process进行拦截操作,完成之后,回调onCotinue执行下一个拦截器,直到执行完毕,或者超时
@Override
public void onContinue(Postcard postcard) {
counter.countDown();
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
postcard.setTag(null == exception ? new HandlerException("No message.") : exception);
counter.cancel();
}
});
}
}
@Override
public void init(final Context context) {
interceptorHasInit = true;
}
//检查是否完成了初始化,有加锁的操作,如果没有完成,会等待10S,这个标志位是在init里面设为true的
private static void checkInterceptorsInitStatus() {
synchronized (interceptorInitLock) {
while (!interceptorHasInit) {
try {
interceptorInitLock.wait(10 * 1000);
} catch (InterruptedException e) {
throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
}
}
}
}
}
一个具体的拦截器,通过postcard里的path来确认是否来拦截对应的postcard,然后对这个postcard做需要做的操作,完成之后回调onContinue继续下一个拦截器。
@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
if ("/test/activity4".equals(postcard.getPath())) {
// 这里的弹窗仅做举例,代码写法不具有可参考价值
final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());
ab.setCancelable(false);
ab.setTitle("温馨提醒");
ab.setMessage("想要跳转到Test4Activity么?(触发了"/inter/test1"拦截器,拦截了本次跳转)");
ab.setNegativeButton("继续", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onContinue(postcard);
}
});
ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onInterrupt(null);
}
});
ab.setPositiveButton("加点料", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
postcard.withString("extra", "我是在拦截器中附加的参数");
callback.onContinue(postcard);
}
});
MainLooper.runOnUiThread(new Runnable() {
@Override
public void run() {
ab.create().show();
}
});
} else {
callback.onContinue(postcard);
}
}
关于拦截器的拦截操作就执行完毕了,并没有直接的调用被注册的拦截器的process函数,而是通过了一个被@Route注解的InterceptorServiceImpl来整体管理拦截器和间接调用到的。
还有一点则是关于拦截器的优先级的问题,可以看到关于拦截器的两个变量interceptorsIndex是一个TreeMap,在put的会校验是否有相同的key,则相当于,不能定义有相同优先级的拦截器,然后拦截器被put进这个TreeMap的时候,会根据起优先级来有序放入。
在InterceptorServiceImpl,interceptorsIndex里面存放的拦截器会被挨着取出来,放到interceptors这个ArrayList里面,然后在执行拦截操作的时候,从interceptors依次出去拦截器,来对postcard执行拦截操作。
class Warehouse {
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
}
public class UniqueKeyTreeMap<K, V> extends TreeMap<K, V> {
private String tipText;
public UniqueKeyTreeMap(String exceptionText) {
super();
tipText = exceptionText;
}
@Override
public V put(K key, V value) {
if (containsKey(key)) {
throw new RuntimeException(String.format(tipText, key));
} else {
return super.put(key, value);
}
}
}