Arouter框架结构

Arouter框架结构中有注解定义和注解处理器相干的内容,Arouter自身也能够算是一个示例。

arouter-api对Arouter初始化

Arouter框架应用的动态注解解决,为适应多模块,应用moduleName后缀生成了一组对立规定的注册类。这些注册类散布在各自的module外部,须要一个治理类把他们聚合在一起,提供对立的注册和调用入口。

初始化入口

集成Arouter路由框架,须要在Application初始化过程中调用上面的办法对Arouter框架进行初始化。

ARouter.init(sInstance);
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;}

动静扫描路由注册类

其中路由表初始化在 LogisticsCenter.init(mContext, executor); 中实现。须要留神上面的这个判断:

ARouter.debuggable() || PackageUtils.isNewVersion(context),在debug或者更新app版本的条件下才会更新路由表,扫描的路由文件列表在SharedPreference中保留。
  • registerByPlugin 是 com.alibaba.arouter 插件标记,表明是否在编译阶段曾经进行了路由表注册工作,间接跳过;
  • 应用 ClassUtils 扫描package(com.alibaba.android.arouter.routes)中所有类文件 - 因为所有模块的路由都创立在这个包门路中;
  • 把所有扫描到的文件,依照规定生成实例,注册到管理器 Warehouse 中;
/** * LogisticsCenter init, load all metas in memory. Demand initialization */public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {    // 有删减    loadRouterMap();    if (!registerByPlugin) {    Set<String> routerMap;        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {            routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);            if (!routerMap.isEmpty()) {                context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();            }            PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.    } else {            routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));        }        for (String className : routerMap) {            if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {                ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {                ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {                ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);            }        }    }}

应用arouter-register插件

Arouter-register是AutoRegister插件在Arouter框架中的实现,次要目标是在编译阶段实现路由表的初始化操作,升高Arouter初始化耗时。

  • Arouter框架生成类包门路: com.alibaba.android.arouter
  • com.android.build.api.transform.TransForm

路由文件和初始化类扫描

@Overridevoid transform(Context context, Collection<TransformInput> inputs , Collection<TransformInput> referencedInputs,               TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {    // 有删减    boolean leftSlash = File.separator == '/'    if (!isIncremental){        outputProvider.deleteAll()    }    inputs.each { TransformInput input ->        // 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)        }    }    if (fileContainsInitClass) {        registerList.each { ext ->            if (!ext.classList.isEmpty()) {                ext.classList.each {                    Logger.i(it)                }                RegisterCodeGenerator.insertInitCodeTo(ext)            }        }    }}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()            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                RegisterTransform.fileContainsInitClass = destFile            }        }        file.close()    }}/** * scan class file * @param class file */static void scanClass(File file) {    scanClass(new FileInputStream(file))}static void scanClass(InputStream inputStream) {    ClassReader cr = new ClassReader(inputStream)    ClassWriter cw = new ClassWriter(cr, 0)    ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw)    cr.accept(cv, ClassReader.EXPAND_FRAMES)    inputStream.close()}
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) {                        //fix repeated inject init code when Multi-channel packaging                        if (!ext.classList.contains(name)) {                            ext.classList.add(name)                        }                    }                }            }        }    }}

指标文件字节码操作

通过下面的扫描操作:获取到Arouter框架生成类的门路寄存于RegisterTransform.registerList中的ScanSetting对象中,以及arouter-api初始化类 LogisticsCenter 所在的文件由 RegisterTransform.fileContainsInitClass 持有。 扫描实现后,调用 RegisterCodeGenerator.insertInitCodeTo(ext) ,遍历RegisterTransform.registerList中的ScanSetting对象作为输出,对 LogisticsCenter 类所在的文件读写操作。
// 读写过程:创立临时文件optJar,从源文件jarFile中读取数据,转写到长期optJar文件中,实现后应用optJar笼罩源文件jarFile。private File insertInitCodeIntoJarFile(File jarFile) {    if (jarFile) {        def optJar = new File(jarFile.getParent(), jarFile.name + ".opt")        if (optJar.exists()){            optJar.delete()        }        def file = new JarFile(jarFile)        Enumeration enumeration = file.entries()        JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar))        while (enumeration.hasMoreElements()) {            JarEntry jarEntry = (JarEntry) enumeration.nextElement()            String entryName = jarEntry.getName()            ZipEntry zipEntry = new ZipEntry(entryName)            InputStream inputStream = file.getInputStream(jarEntry)            jarOutputStream.putNextEntry(zipEntry)            if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {                def bytes = referHackWhenInit(inputStream)                jarOutputStream.write(bytes)            } else {                jarOutputStream.write(IOUtils.toByteArray(inputStream))            }            inputStream.close()            jarOutputStream.closeEntry()        }        jarOutputStream.close()        file.close()        if (jarFile.exists()) {            jarFile.delete()        }        optJar.renameTo(jarFile)    }    return jarFile}// 找到com/alibaba/android/arouter/core/LogisticsCenter.class,调用这个办法private byte[] referHackWhenInit(InputStream inputStream) {    ClassReader cr = new ClassReader(inputStream)    ClassWriter cw = new ClassWriter(cr, 0)    ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw)    cr.accept(cv, ClassReader.EXPAND_FRAMES)    return cw.toByteArray()}// 在LogisticsCenter.class中查找 loadRouterMap 办法class MyClassVisitor extends ClassVisitor {    MyClassVisitor(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)    }    @Override    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    }}// 向 loadRouterMap 插入语句:retister(className)// register函数在 LogisticsCenter 中定义,用于把给定的类名生成实例,注册到 Warehouse 管理器中。class RouteMethodVisitor extends MethodVisitor {    RouteMethodVisitor(int api, MethodVisitor mv) {        super(api, mv)    }    @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)    }    @Override    void visitMaxs(int maxStack, int maxLocals) {        super.visitMaxs(maxStack + 4, maxLocals)    }}

arouter-register与arouter-api 初始化Arouter

Arouter-register是AutoRegister的一个实现,能够参考\# AutoRegister框架剖析。

  • Arouter-api在运行时,通过扫描类文件进行初始化操操作。
  • Arouter-register在编译阶段的序幕局部,扫描jar文件和.class文件找到路由注册类,在 LogisticsCenter.class#loadRouterMap() 办法中插入LogisticsCenter.class#register(className) 调用语句,使得loadRouterMap能够间接实现路由注册,节俭了运行时扫描的工夫。
  • Arouter-register工作在Arouter-api的根底上,register(className)和 loadRouterMap()都由Arouter-api库提供。

插件中应用的办法记录

JarFile

  • file.entries() -> Enumeration : Jar文件中打包的.class文件汇合
  • enumeration.nextElement() -> JarEntry : Jar文件中打包的.class 文件

类拜访

应用 ClassVisiter、ClassReader、ClassWriter 对.class文件的读写操作,都在这一组类中。

void scanClass(InputStream inputStream) {    ClassReader cr = new ClassReader(inputStream)    ClassWriter cw = new ClassWriter(cr, 0)    ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw)    cr.accept(cv, ClassReader.EXPAND_FRAMES)    inputStream.close()}byte[] referHackWhenInit(InputStream inputStream) {    ClassReader cr = new ClassReader(inputStream)    ClassWriter cw = new ClassWriter(cr, 0)    ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw)    cr.accept(cv, ClassReader.EXPAND_FRAMES)    return cw.toByteArray()}static class MyClassVisitor extends ClassVisitor {    ScanClassVisitor(int api, ClassVisitor cv) {        super(api, cv)    }    @Override    void visit(int version, int access, String name, String signature,               String superName, String[] interfaces) {        super.visit(version, access, name, signature, superName, interfaces)    }    @Override    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    }}

办法拜访

应用 MethodVisitor 对类办法进行读写,能够插入代码。

class RouteMethodVisitor extends MethodVisitor {    RouteMethodVisitor(int api, MethodVisitor mv) {        super(api, mv)    }    @Override    void visitInsn(int opcode) {        super.visitInsn(opcode)    }    @Override    void visitMaxs(int maxStack, int maxLocals) {        super.visitMaxs(maxStack + 4, maxLocals)    }}

ASM库

Arouter-register插件,是基于字节码插桩技术实现的。ASM是一个插桩框架,框架集成在 com.android.tools.build:gradle 内,下表有删减。
\--- com.android.tools.build:gradle:2.1.3     \--- com.android.tools.build:gradle-core:2.1.3          +--- com.android.tools.build:builder:2.1.3          |    +--- org.ow2.asm:asm:5.0.3          |    \--- org.ow2.asm:asm-tree:5.0.3          |         \--- org.ow2.asm:asm:5.0.3          +--- org.ow2.asm:asm:5.0.3          +--- org.ow2.asm:asm-commons:5.0.3          |    \--- org.ow2.asm:asm-tree:5.0.3 (*)          +--- net.sf.proguard:proguard-gradle:5.2.1          |    \--- net.sf.proguard:proguard-base:5.2.1          +--- org.jacoco:org.jacoco.core:0.7.6.201602180812          |    \--- org.ow2.asm:asm-debug-all:5.0.4          \--- org.antlr:antlr:3.5.2               +--- org.antlr:antlr-runtime:3.5.2               \--- org.antlr:ST4:4.0.8 (*)

相干教程

Android根底系列教程:
Android根底课程U-小结_哔哩哔哩_bilibili
Android根底课程UI-布局_哔哩哔哩_bilibili
Android根底课程UI-控件_哔哩哔哩_bilibili
Android根底课程UI-动画_哔哩哔哩_bilibili
Android根底课程-activity的应用_哔哩哔哩_bilibili
Android根底课程-Fragment应用办法_哔哩哔哩_bilibili
Android根底课程-热修复/热更新技术原理_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/7044826883719954469,如有侵权,请分割删除。