ARouter实现分析

网络上已经有很多分析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.Transformtransform-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)

有个疑问:通过类的字符串名称去反射类,会受代码混淆影响吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting Horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值