当前位置:网站首页>I Arouter framework analysis
I Arouter framework analysis
2022-06-23 03:18:00 【User 9253515】
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 .
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 .
- 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 .
- Bytecode piling technology
- ASM: A framework implementation of bytecode instrumentation
\--- 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 (*)
In this paper, from https://juejin.cn/post/7044826883719954469, If there is any infringement , Please contact to delete .
边栏推荐
- Application of map function in JS
- Communication between containers flannel and calico comparison
- Integrated solution for intelligent supply chain platform management in rubber industry
- [SaaS examination certification] apaas_ Tencent Qianfan magic pen
- Redis source code reading (I) general overview
- What is the difference between JS undefined and null
- Why don't I suggest you use "! = null" to judge empty space?
- CentOS install redis
- January 31, 2022: Maze III. There is a ball in the maze of open spaces and walls. ball
- Zoom/skype/ nailing / live broadcast / conference / online video real-time subtitle generation and translation, simultaneous interpretation
猜你喜欢
![Analysis of China's integrated circuit industry chain in 2021: huge downstream market demand [figure]](/img/de/d73805aaf4345ca3d2a7baf85aab8d.jpg)
Analysis of China's integrated circuit industry chain in 2021: huge downstream market demand [figure]

Analysis on the development of China's graphene industry chain in 2021: with the support of energy conservation and environmental protection policies, the scale of graphene industry will continue to e
![Analysis on demand and market scale of China's steamed stuffed bun industry in 2020 [figure]](/img/4b/dd272f98b89a157180bf68570d2763.jpg)
Analysis on demand and market scale of China's steamed stuffed bun industry in 2020 [figure]
![Analysis on development history, industrial chain, output and enterprise layout of medical polypropylene in China in 2020 [figure]](/img/28/ebfc25ec288627706e15a07e6bdb77.jpg)
Analysis on development history, industrial chain, output and enterprise layout of medical polypropylene in China in 2020 [figure]
![Analysis on the development status of China's watch industry in 2021: a large number of electric watches are imported [figure]](/img/ca/672bfe49c8123da8679b2abeb43a2e.jpg)
Analysis on the development status of China's watch industry in 2021: a large number of electric watches are imported [figure]

Analysis on the development of China's satellite navigation industry chain in 2021: satellite navigation is fully integrated into production and life, and the satellite navigation industry is also boo
随机推荐
Handlebars dynamic adjustment
Weekly Postgres world news 2022w03
YouTube security scenarios
Quickly understand the development status of secondary nodes of industrial Internet identity analysis
The primary level of SAP retail uses the transaction code wrfmatcopy to create commodity master data
Reading redis source code (V) master-slave replication and sentinel mechanism
JS event bubble and event capture
The difference between the use of return, break and continue in the if statement in JS
January 31, 2022: Maze III. There is a ball in the maze of open spaces and walls. ball
JS counts the number of times a string appears in another string
Goframe framework (RK boot): realize distributed log tracing
MIT 6. S081/fall 2020 build risc-v and xv6 development and debugging environment
Learning records - things inherited by subclass parent of C #
CFS After the CHM file is opened, the hyperlink content cannot be loaded and blank is displayed
Pytest common summary
"Tianzhou II" successfully docked! Three minutes to understand the shocking black technology on "Tianzhou II"! Headlines
Some people are crazy, others are running away, and open source software is both hot and cold
New uniapp+uniui background management uniuadmin
Why can only a small number of condition type prices be maintained in me12 of SAP mm?
Quickly grab the red envelope cover of Tencent blue whale New Year! Slow hands!