ARouter-初始化

初始化主要是指如何將APT生成的路由類,攔截器類和服務提供類註冊到內存中,供運行時使用。java

ARuter提供了兩種方式,分別是gradle插件自動註冊和掃描dex文件。android

使用gradle插件進行自動註冊

PluginLaunch定義了Gradle插件,其中配置了3個接口是須要進行掃描和插入代碼的:api

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進行了代碼掃描和插入兩個工做:數據結構

代碼掃描流程:app

  1. 調用ScanUtil.scanJar()掃描jar包
  2. 調用ScanUtil.scanClass()掃描全部class文件
  3. ScanClassVisitor掃描每一個class類,進行接口判斷,若是在RegisterTransform.registerList配置的接口中,就蒐集該類
  4. 掃描時須要判斷class是否在com/alibaba/android/arouter/routes/的包下
  5. 在搜索jar包時,順便把LogisticsCenter所在的文件找到,方便代碼插入到這個類中

代碼插入流程:ide

  1. 調用RegisterCodeGenerator.insertInitCodeTo()方法進行代碼插入到LogisticsCenter的class文件中gradle

  2. 經過MyClassVisitor和RouteMethodVisitor將實現了配置的接口的類的註冊過程插入到LogisticsCenter.loadRouterMap()方法中,註冊方法是調用了LogisticsCenter.register()方法spa

    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)
        }
    }
    複製代碼
  3. 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生成的在特定包名路徑下的類。

  1. 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;
        }
    複製代碼
  2. 使用線程池加載每一個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();
    複製代碼
  3. 掃描出全部的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中。

相關文章
相關標籤/搜索