Arouter should be considered as the Android national level framework, in their own componentized framework, but also referred to a lot of Arouter’s design, in reading the source code, feel that some points can be optimized, so there is today’s article.

1. Confusion optimization





Arouter code

The RouteMethodVisitor class of the Arouter- Gradle-Plugin module will inject all the collected classes into the loadRouterMap method of LogisticsCenter:

 @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)/ / the name of the class
             // 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)
  }
Copy the code

The injection results are as follows:

private static void loadRouterMap(a) {
    registerByPlugin = false;
    // Just to give you a rough example
    register("com.alibaba.android.arouter.routes.ARouter? Root? modulejava"); . }Copy the code

The final reflection initialization is done using the register method:

 private static void register(String className) {
        if(! TextUtils.isEmpty(className)) {try{ Class<? > clazz = Class.forName(className); Object obj = clazz.getConstructor().newInstance();if (obj instanceofIRouteRoot) { registerRouteRoot((IRouteRoot) obj); }... }}Copy the code

Optimization Suggestions

  • Replace the String className with Class

We can start with the RouteMethodVisitor of Gradle-Plugin to change the RouteMethodVisitor:

@Override
void visitInsn(int opcode) {
    //generate code before return
    if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
        extension.classList.each { name ->
            / / for the Class
            mv.visitLdcInsn(Type.getType("L" + name + ";"))
            // generate invoke register method into LogisticsCenter.loadRouterMap()
            mv.visitMethodInsn(Opcodes.INVOKESTATIC
                               , ScanSetting.GENERATE_TO_CLASS_NAME
                               , ScanSetting.REGISTER_METHOD_NAME
                               , "(Ljava/lang/Class;) V"
                               , false)}}super.visitInsn(opcode)
}
Copy the code

The injection results are as follows:

private static void loadRouterMap(a) {
    registerByPlugin = false;
    // Just to give you a rough example
    register(com.alibaba.android.arouter.routes.ARouter?Root?modulejava.class);
    ... 
}
Copy the code

Also change the register method as follows:

private static void register(Class clazz){
   if(clazz! =null) {
        try {
           Object obj = clazz.getConstructor().newInstance();
            if (obj instanceofIRouteRoot) { registerRouteRoot((IRouteRoot) obj); .Copy the code

2. Reflection optimization

While the confusion problem can be solved with Class, it still does not solve the performance problems caused by reflection. We can get the name of the class when we insert the ASM pin. Can we initialize the class with a new class ()? We can continue to solve the reflection problem along the same lines as above. Moving on to the RouteMethodVisitor, let’s change the RouteMethodVisitor:

@Override
void visitInsn(int opcode) {
    //generate code before return
    if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
        extension.classList.each { name ->
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            // Create an instance with the no-argument constructor
            mv.visitTypeInsn(Opcodes.NEW, name)
            mv.visitInsn(Opcodes.DUP)
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, name, "<init>"."()V".false)

            String interfaceName = ""
            String insertMethod = ""
            / / (1),
            if (name.contains("ARouter\$\$Root\$\$")) {
                    interfaceName = "com/alibaba/android/arouter/facade/template/IRouteRoot"
                    insertMethod = "registerRouteRoot"
             } else if (name.contains("ARouter\$\$Interceptors\$\$")) {
                    interfaceName = "com/alibaba/android/arouter/facade/template/IInterceptorGroup"
                    insertMethod = "registerInterceptor"
             } else if (name.contains("ARouter\$\$Providers\$\$")) {
                    interfaceName = "com/alibaba/android/arouter/facade/template/IProviderGroup"
                    insertMethod = "registerProvider"
             }
			
            if(! TextUtils.isEmpty(interfaceName) && ! TextUtils.isEmpty(insertMethod)) { mv.visitMethodInsn(Opcodes.INVOKESTATIC , ScanSetting.GENERATE_TO_CLASS_NAME , insertMethod ,"(L${interfaceName};) V"
                                       , false)}}}super.visitInsn(opcode)
 }
Copy the code

(3) : Interceptors/Providers/Root/Interceptors/Providers/apt/asp/asp/asp/asp/asp/asp/asp/asp We also need an insertMethod here, because we’re a new Class (), we don’t have the same Class as a register method, we need an explicit type to inject, so we use an interface here. Injection effect is as follows:

private static void loadRouterMap(a) {
    registerByPlugin = false;
    registerRouteRoot(newcom.alibaba.android.arouter.routes.ARouter? Root? modulejava()); registerInterceptor(newcom.alibaba.android.arouter.routes.ARouter? Interceptors? app()); registerProvider(new com.alibaba.android.arouter.routes.ARouter?Providers?app());
    ... 
}
Copy the code

3. Some interesting issues

1.issue 776: Kt injection Autowired is invalid

There are kotlin-Java compatibility issues with Arouter Autowired injection

Var showBadge: Boolean? Var showBadge: Boolean? = false Indicates that the injection succeeds

The reason: In the arouter generated code, ShowBadge = substitut.getintent ().getBooleanextra (“showBadge”, substitut.showbadge), GetBooleanExtra (String Name, Boolean defaultValue) ¶ getBooleanExtra(String Name, Boolean defaultValue) ¶ Error can’t unbox a null value

2,issue 818: Confusion over using Arouter in AndroidX projects

This is my own 🤣, mainly because arouter wrote the path of support. Fragment when looking at the source code of arouter- Compiler. [bug Mc-10862] – Jetifier will not replace constant support with Androidx. Fragment. Because I wrote a support-to-AndroidX demo and tested it… To supplement!