共计 11215 个字符,预计需要花费 29 分钟才能阅读完成。
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
路由文件和初始化类扫描
@Override
void 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,如有侵权,请分割删除。