初始化主要是指如何将APT生成的路由类,拦截器类和服务提供类注册到内存中,供运行时使用。
ARuter提供了两种方式,分别是gradle插件自动注册和扫描dex文件。
使用gradle插件进行自动注册
PluginLaunch定义了Gradle插件,其中配置了3个接口是需要进行扫描和插入代码的:
ArrayList<ScanSetting> list = new ArrayList<>(3)
list.add(new ScanSetting('IRouteRoot'))
list.add(new ScanSetting('IInterceptorGroup'))
list.add(new ScanSetting('IProviderGroup'))
RegisterTransform.registerList = list
RegisterTransform进行了代码扫描和插入两个工作:
代码扫描流程:
- 调用ScanUtil.scanJar()扫描jar包
- 调用ScanUtil.scanClass()扫描所有class文件
- ScanClassVisitor扫描每个class类,进行接口判断,如果在RegisterTransform.registerList配置的接口中,就搜集该类
- 扫描时需要判断class是否在com/alibaba/android/arouter/routes/的包下
- 在搜索jar包时,顺便把LogisticsCenter所在的文件找到,方便代码插入到这个类中
代码插入流程:
-
调用RegisterCodeGenerator.insertInitCodeTo()方法进行代码插入到LogisticsCenter的class文件中
-
通过MyClassVisitor和RouteMethodVisitor将实现了配置的接口的类的注册过程插入到LogisticsCenter.loadRouterMap()方法中,注册方法是调用了LogisticsCenter.register()方法
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) } } -
register()方法会根据具体的接口注册到对应的数据结构中:
private static void register(String className) { if (!TextUtils.isEmpty(className)) { try { Class<?> clazz = Class.forName(className); Object obj = clazz.getConstructor().newInstance(); if (obj instanceof IRouteRoot) { registerRouteRoot((IRouteRoot) obj); } else if (obj instanceof IProviderGroup) { registerProvider((IProviderGroup) obj); } else if (obj instanceof IInterceptorGroup) { registerInterceptor((IInterceptorGroup) obj); } else { logger.info(TAG, "register failed, class name: " + className + " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup."); } } catch (Exception e) { logger.error(TAG,"register class error:" + className); } } }通过ASM代码扫描和插入,实现了路由和服务的自动注册。
通过读取dex文件,进行注册类的搜集和代码插入
如果没有使用gradle插件实现自动注册,那么ARouter会在初始化时扫描dex文件,把实现了对应接口的类都找到,并进行初始化。
具体逻辑在LogisticsCenter.init()方法中。
调用ClassUtils.getFileNameByPackageName()方法找到APT生成的在特定包名路径下的类。
-
ClassUtils.getSourcePaths()找到应用的所有dex文件的路径(其中处理了MultiDex的生成多dex的场景)
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException { ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0); File sourceApk = new File(applicationInfo.sourceDir); List<String> sourcePaths = new ArrayList<>(); sourcePaths.add(applicationInfo.sourceDir); //add the default apk path //the prefix of extracted file, ie: test.classes String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT; // 如果VM已经支持了MultiDex,就不要去Secondary Folder加载 Classesx.zip了,那里已经么有了 // 通过是否存在sp中的multidex.version是不准确的,因为从低版本升级上来的用户,是包含这个sp配置的 if (!isVMMultidexCapable()) { //the total dex numbers int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1); File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME); for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) { //for each dex file, ie: test.classes2.zip, test.classes3.zip... String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX; File extractedFile = new File(dexDir, fileName); if (extractedFile.isFile()) { sourcePaths.add(extractedFile.getAbsolutePath()); //we ignore the verify zip part } else { throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'"); } } } if (ARouter.debuggable()) { // Search instant run support only debuggable sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo)); } return sourcePaths; } -
使用线程池加载每个dex文件,遍历所有的class文件,找到对应包名(“com.alibaba.android.arouter.routes”)下的类(使用CountDownLatch等待所有dex文件扫描完成)
for (final String path : paths) { DefaultPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { DexFile dexfile = null; try { if (path.endsWith(EXTRACTED_SUFFIX)) { //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache" dexfile = DexFile.loadDex(path, path + ".tmp", 0); } else { dexfile = new DexFile(path); } Enumeration<String> dexEntries = dexfile.entries(); while (dexEntries.hasMoreElements()) { String className = dexEntries.nextElement(); if (className.startsWith(packageName)) { classNames.add(className); } } } catch (Throwable ignore) { Log.e("ARouter", "Scan map file in dex files made error.", ignore); } finally { if (null != dexfile) { try { dexfile.close(); } catch (Throwable ignore) { } } parserCtl.countDown(); } } }); } parserCtl.await(); -
扫描出所有的class文件之后加载到内存中
for (String className : routerMap) { if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { // This one of root elements, load root. ((IRouteRoot)(Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) { // Load interceptorMeta ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) { // Load providerIndex ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } }路由,拦截器,服务提供者存储到对应的map中。