当前位置:网站首页>Arouter framework analysis
Arouter framework analysis
2022-06-23 21:22:00 【User 9227784】
Arouter Frame structure
Arouter There are annotation definitions and annotation processor related contents in the framework structure ,Arouter It can also be regarded as an example .
arouter-api Yes Arouter initialization
Arouter Static annotation processing used by the framework , To adapt to multiple modules , Use moduleName Suffixes generate a set of registration classes with uniform rules . These registration classes are distributed in their respective module Inside , You need a management class to aggregate them together , Provide unified registration and call entry .
Initialization entry
Integrate Arouter Routing framework , Need to be in Application The following method is called during initialization. Arouter The framework is initialized .
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;
}Dynamic scanning route registration class
The routing table is initialized in LogisticsCenter.init(mContext, executor); Finish in . We need to pay attention to the following judgment :
ARouter.debuggable() || PackageUtils.isNewVersion(context), stay debug Or update app The routing table will only be updated under the condition of version , The list of scanned routing files is in SharedPreference Kept in .
- registerByPlugin yes com.alibaba.arouter Plug in tags , Indicate whether the routing table has been registered in the compilation stage , Just skip ;
- Use ClassUtils scanning package(com.alibaba.android.arouter.routes) All class files in - Because the routes of all modules are created in this packet path ;
- Put all the scanned files , Generate instances according to rules , Register with manager Warehouse in ;
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
// With deletion
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);
}
}
}
}Use arouter-register plug-in unit
Arouter-register yes AutoRegister Plug in Arouter Implementation in framework , The main purpose is to complete the initialization of the routing table in the compilation stage , Reduce Arouter Initialization time .
- Arouter The path of the class package generated by the framework : com.alibaba.android.arouter
- com.android.build.api.transform.TransForm
Route file and initialization class scanning
@Override
void transform(Context context, Collection<TransformInput> inputs , Collection<TransformInput> referencedInputs,
TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
// With deletion
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)
}
}
}
}
}
}
}Object file bytecode operation
Through the above scanning operation : Get Arouter The path of the framework generated class is stored in RegisterTransform.registerList Medium ScanSetting In the object , as well as arouter-api Initialization class LogisticsCenter Where the document is located RegisterTransform.fileContainsInitClass hold . After scanning , call RegisterCodeGenerator.insertInitCodeTo(ext) , Traverse RegisterTransform.registerList Medium ScanSetting Object as input , Yes LogisticsCenter Read and write operations of the file where the class is located .
// Reading and writing process : Create temporary file optJar, From the source file jarFile Read data from , Transcribe to temporary optJar In file , Use... When finished optJar Overwrite source file 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
}
// find com/alibaba/android/arouter/core/LogisticsCenter.class, Call this method
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()
}
// stay LogisticsCenter.class Search for loadRouterMap Method
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
}
}
// towards loadRouterMap Insert statement :retister(className)
// register Function in LogisticsCenter In the definition of , Used to generate an instance of a given class name , Sign up to Warehouse Manager .
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)// Class 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 And arouter-api initialization Arouter
Arouter-register yes AutoRegister An implementation of , You can refer to \# AutoRegister Frame analysis .
- Arouter-api At run time , Initialize by scanning class files .
- Arouter-register At the end of the compilation phase , scanning jar Document and .class The file found the route registration class , stay LogisticsCenter.class#loadRouterMap() Method LogisticsCenter.class#register(className) Call statement , bring loadRouterMap You can complete the route registration directly , Saves runtime scanning time .
- Arouter-register Working in Arouter-api On the basis of ,register(className) and loadRouterMap() All by Arouter-api Library provides .
The method used in the plug-in records
JarFile
- file.entries() -> Enumeration : Jar Packaged in a file .class File set
- enumeration.nextElement() -> JarEntry : Jar Packaged in a file .class file
Such access
Use ClassVisiter、ClassReader、ClassWriter Yes .class Read and write operation of the file , All in this group of classes .
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
}
}Method access
Use MethodVisitor Read and write class methods , You can insert code .
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 library
Arouter-register plug-in unit , It is based on bytecode stake insertion technology .ASM It is a pile inserted frame , The framework is integrated in com.android.tools.build:gradle Inside , The following table has been deleted .
\--- 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 (*)Related courses
Android Basic series of tutorials :
Android basic course U- Summary _ Bili, Bili _bilibili
Android basic course UI- Layout _ Bili, Bili _bilibili
Android basic course UI- Control _ Bili, Bili _bilibili
Android basic course UI- Animation _ Bili, Bili _bilibili
Android basic course -activity Use _ Bili, Bili _bilibili
Android basic course -Fragment Usage method _ Bili, Bili _bilibili
In this paper, from https://juejin.cn/post/7044826883719954469, If there is any infringement , Please contact to delete .
边栏推荐
- Gin security -3: fast implementation of CSRF verification
- JS to get the screen size, current web page and browser window
- What is the difference between object storage and cloud disk service? What are the functions of cloud disk service?
- Global and Chinese markets of natural starch 2022-2028: Research Report on technology, participants, trends, market size and share
- . NET Framework . Net core and Net standard
- What if there are too few jetpack compose theme colors? Design your own color system
- 数字电路概述
- Elegant asynchronous programming version answer async and await parsing
- What is the role of short video AI intelligent audit? Why do I need intelligent auditing?
- Customize view to imitate today's headlines and like animation!
猜你喜欢
![Harmonyos application development -- mynotepad[memo][api v6] based on textfield and image pseudo rich text](/img/b1/71cc36c45102bdb9c06e099eb42267.jpg)
Harmonyos application development -- mynotepad[memo][api v6] based on textfield and image pseudo rich text

How PMO uses two dimensions for performance appraisal

Four aspects of PMO Department value assessment
Application of JDBC in performance test

How to view the role of PMO in agile organizations?

How does PMO select and train project managers?

New SQL syntax quick manual!

What are the main dimensions of PMO performance appraisal?

I am 30 years old, no longer young, and have nothing

Gradle asked seven times. You should know that~
随机推荐
How to deal with unclear pictures? What are the techniques for taking clear pictures?
[sap-hcm] report jump to pa30/pa40 instance
How to build a personal cloud game server? How many games can the cloud game platform install?
What software is safe to use to fight new debts? What are the new bond platforms
SAP retail uses transaction code wb03 to display a factory configured in the background
Stm32 w5500 implements TCP, DHCP and web server
JS takes two decimal places
同花顺开户是安全的吗?
Where should DNS start? I -- from the failure of Facebook
How to solve the problem of large traffic audio audit? What are the common approval methods?
Steps for formulating the project PMO strategic plan
Whether the offsetwidth includes scroll bars
SAP FICO as03 display fixed assets master data
2021 three "myths" of science and technology industry -- yuancosmos, NFT and web 3
Copilot - employee, your layoff notice has been delivered
How to separate image processing? What should I pay attention to when separating layers?
How to evaluate performance optimization? Covering too much knowledge?
How to process the text of a picture into a table? Can the text in the picture be transferred to the document?
Why is it invalid to assign values to offsetwidth and offsetHeight
Use of pathinfo/pathname in nodejs