网络上已经有很多分析ARouter的文章了,但是各有偏重;这里整理一些关键技术点,尽量能够将完整的流程都覆盖到,并且将一些容易错误使用的细节列举出来。
Java注解处理
ARouter大量使用了Java注解,并且通过APT(Annotation Processing Tool )自动处理注解,生成完成路由功能的代码。
ARouter使用的是RetentionPolicy.CLASS形式的注解,也就是注解只存在于编译阶段,生成的class文件中没有这些注解了。
ARouter APT的实现位于 arouter-compiler 中,比如处理路由注解(@Route)的 RouteProcessor,同时也处理注入注解(@Autowired)。
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
}
APT自动生成代码,生成的代码在 build/generated/source/apt/com/alibaba/android/arouter/routes 目录下,具体代码在后面分析。
ARouter APT处理不需要 gradle 插件机制,默认 Java 编译就有针对 APT 的编译阶段,只要在项目依赖中声明 APT 处理工具,APT 处理生成的代码可以看成项目代码的依赖。
dependencies {
annotationProcessor "com.alibaba:arouter-compiler:1.2.2"
}
路由机制
路由注册
APT自动生成代码在 build/generated/source/apt/com/alibaba/android/arouter/routes 目录下,通过这些代码可以理解ARoute实现的细节。
路由根 ARouter\$$Root\$$test 注册所有的路由分组 IRouteGroup
public class ARouter$$Root$$test implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("test", ARouter$$Group$$test.class);
}
}
下面是一个路由分组 ARouter\$$Group\$$test,路由分组注册 Activity 路由
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/XXXActivity", RouteMeta.build(RouteType.ACTIVITY, XXXActivity.class, "/test/XXXactivity", "test", null, -1, -2147483648));
atlas.put("/test/YYYActivity/", RouteMeta.build(RouteType.ACTIVITY, YYYActivity.class, "/test/YYYactivity/", "test", null, -1, -2147483648));
atlas.put("/test/ZZZActivity", RouteMeta.build(RouteType.ACTIVITY, ZZZActivity.class, "/test/ZZZactivity", "test", null, -1, -2147483648));
}
}
对应服务的 IProviderGroup:IProvider也会在路由中注册,所以既可以通过接口类找到,也可以通过路由找到
public class ARouter$$Providers$$test implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.test.data.Data", RouteMeta.build(RouteType.PROVIDER, Data.class, "/test/data", "test", null, -1, -2147483648));
}
}
对应拦截器的 IInterceptorGroup:IInterceptor有优先级,这里的Map是 UniqueKeyTreeMap
public class ARouter$$Interceptors$$classroom implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(8, TestInterceptor.class);
}
}
路由执行
ARouter 在运行时处理路由的代码在 arouter-api 模块中。
- 初始化
在 LogisticsCenter.init 中,三种类型的信息分别收集到 Warehouse 中的 groupsIndex/routes、providersIndex/providers和interceptorsIndex/interceptors。在初始化时,每种类型只收集第一层信息。
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);
}
}
- 二次加载
实例化具体的IRouteGroup,并二次加载 RouteMeta
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
completion(postcard); // Reload
实例化IProvider,IProvider是单例,比如InterceptorService、AutowiredService 等 service 都是IProvider。(IProvider其实应该改成IService更妥帖些)
IProvider instance = Warehouse.providers.get(providerMeta);
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
postcard.setProvider(instance);
实例化 IInterceptor,InterceptorServiceImpl.init 中
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);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
- 路由拦截
路由拦截发送在 _ARouter.navigation 时,绿色通道的组件不拦截;IProivder、Fragment都有绿色通道。
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
...
});
}
在线程池中等待拦截处理完成
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_excute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
}
}
}
多个拦截器是串行处理的
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
counter.countDown();
_excute(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.getMessage()); // save the exception message for backup.
counter.cancel();
}
});
注入机制
注入由服务 AutowiredServiceImpl 处理,具体每一个类的注入由对应的 ISyringe 处理注入。
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
注解处理自动生成 com.test.XXXActivity\$\$ARouter\$\$Autowired 这样的类
public class StudentDetailActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
TestActivity substitute = (TestActivity)target;
substitute.service1 = ARouter.getInstance().navigation(AutowiredService.class);
if (substitute.service1 == null) {
throw new RuntimeException("The field 'service1' is null, in class '" + TestActivity.class.getName() + "!");
}
substitute.service2 = (AutowiredService)ARouter.getInstance().build("/arouter/service/autowired").navigation();
substitute.object1 = (com.test.model.bean.object1Bean) substitute.getIntent().getSerializableExtra("object1");
if (null != serializationService) {
substitute.object2 = serializationService.parseObject(substitute.getIntent().getStringExtra("object2"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestActivity.TestClass>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'object2' in class 'TestActivity' , then you should implement 'SerializationService' to support object auto inject!");
}
}
}
特殊类型的注入
- 服务 IProvider:可以通过服务名称或者接口类型定位
- 序列化对象:需要自行实现AutowiredService
其他机制
- 自定义类加载
注册 ClassLoaderService 处理自定义类加载,但是没有发现使用的地方。
- 集中处理路由丢失
注册 DegradeService 处理路由丢失。在 _ARouter.navigation 中被引用,如果调用navigation已经指定NavigationCallback,则改由callback处理。
- 路径重写
注册 PathReplaceService 处理路径重写。在 _ARouter.build 中被引用。
自动注册Gradle插件
ARouter 插件完成最后的路由自动注册,实现位于 arouter-gradle-plugin 中。
通过 ARouter gradle 插件源码,我们能够大致理解 gradle 插件的实现过程。
在 resources/META-INF/gradle-plugins 目录下有一个 com.alibaba.arouter.properties 文件,内容如下:
implementation-class=com.alibaba.android.arouter.register.launch.PluginLaunch
由此可知, Gradle 通过 gradle-plugins 目录识别其插件,这里有一个插件 com.alibaba.arouter,实现类是 PluginLaunch代码中有 PluginLaunch.groovy 文件,是具体功能实现:
import com.android.build.gradle.AppExtension
public class PluginLaunch implements Plugin<Project> {
@Override
public void apply(Project project) {
def android = project.extensions.getByType(AppExtension)
android.registerTransform(transformImpl)
}
}
ARouter gradle 插件依赖 com.android.tools.build:gradle 插件,该插件应用于 android apk 构建的 Transform 阶段。
从1.5.0-beta1
开始,android的gradle插件引入了com.android.build.api.transform.Transform
(transform-api),可以用于在android 打包、class转换成dex过程中,加入开发者自定义的处理逻辑。
Transform 的输入有 jar 和 dir (目录)两种类型:
ARouter 对 Jar 的处理:扫描Jar类似于遍历目录
// 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)
ARouter 对目录的处理:
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)
扫描找到文件 'com/alibaba/android/arouter/core/LogisticsCenter.class';同时找到所有包名为 com/alibaba/android/arouter/routes 的 class 文件,并按接口分类保存在三个集合中:
ArrayList<ScanSetting> list = new ArrayList<>(3)
list.add(new ScanSetting('IRouteRoot'))
list.add(new ScanSetting('IInterceptorGroup'))
list.add(new ScanSetting('IProviderGroup'))
RegisterTransform.registerList = list
static class ScanClassVisitor extends ClassVisitor {
ScanClassVisitor(int api, ClassVisitor cv) {
super(api, cv)
}
void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces)
RegisterTransform.registerList.each { ext ->
if (ext.interfaceName && interfaces != null) {
interfaces.each { itName ->
if (itName == ext.interfaceName) {
ext.classList.add(name)
}
}
}
}
}
}
最后插入注册代码:
static void insertInitCodeTo(ScanSetting registerSetting) {
if (registerSetting != null && !registerSetting.classList.isEmpty()) {
RegisterCodeGenerator processor = new RegisterCodeGenerator(registerSetting)
File file = RegisterTransform.fileContainsInitClass
if (file.getName().endsWith('.jar'))
processor.insertInitCodeIntoJarFile(file)
}
}
在jar中找到LogisticsCenter,改写它
if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {
Logger.i('Insert init code to class >> ' + entryName)
def bytes = referHackWhenInit(inputStream)
jarOutputStream.write(bytes)
} else {
jarOutputStream.write(IOUtils.toByteArray(inputStream))
}
其中的 referHackWhenInit,通过 ClassVisitor和MethodVisitor找到 loadRouterMap方法,并修改它
class MyClassVisitor extends ClassVisitor {
MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions)
//generate code into this method
if (name == ScanSetting.GENERATE_TO_METHOD_NAME) {
mv = new RouteMethodVisitor(Opcodes.ASM5, mv)
}
return mv
}
}
class RouteMethodVisitor extends MethodVisitor {
@Override
void visitInsn(int opcode) {
//generate code before return
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
extension.classList.each { name ->
name = name.replaceAll("/", ".")
mv.visitLdcInsn(name)//类名
// generate invoke register method into LogisticsCenter.loadRouterMap()
mv.visitMethodInsn(Opcodes.INVOKESTATIC
, ScanSetting.GENERATE_TO_CLASS_NAME
, ScanSetting.REGISTER_METHOD_NAME
, "(Ljava/lang/String;)V"
, false)
}
}
super.visitInsn(opcode)
}
}
上面在 return 语句前面插入了代码, 插入两个指令:
- visitLdcInsn:加载类名称(路由类)到寄存器
- visitMethodInsn:调用方法,这里是 com.alibaba.android.arouter.core.LogisticsCenter.register(String className)
有个疑问:通过类的字符串名称去反射类,会受代码混淆影响吗?