Arouter初始化過程

Arouter初始化過程

Arouter初始化入口是Arouter類中的init方法,但Arouter類只是個代理類,而被代理的是和它同包下的_Arouter類,看下_Arouter的init方法:

protected static synchronized boolean init(Application application) {
    mContext = application;
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    
    return true;
}

_Arouter類把初始化工作轉(zhuǎn)交給了LogisticsCenter的init方法,該方法主要做了2件事:

1.獲取指定包名的類文件(編譯期部分自動生成的類,不包括AutowiredProcessor生成的)。

2.將上述獲取的類文件按條件緩存起來(緩存到Warehouse類中)

先分析第一步,截取LogisticsCenter的init方法的部分代碼:

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;

    try {
        long startInit = System.currentTimeMillis();
        Set<String> routerMap; //存儲類文件的集合

        // It will rebuild router map every times when debuggable.
        //第一次安裝isNewVersion返回的是true,之后會緩存當(dāng)前的versionName和versionCode
        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
          
            
            //ROUTE_ROOT_PAKCAGE的值是com.alibaba.android.arouter.routes
            //該方法是獲取指定包名下的所有類文件
            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();
            }

            //緩存當(dāng)前的版本信息
            PackageUtils.updateVersion(context);    // Save new version name when router map update finish.
        } else {
            //版本沒更新,且非Debug模式下,直接讀取緩存的類文件集合
            routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
        }       
      //...省略其他代碼
    } catch (Exception e) {
      //...
    }
    //...
}

繼續(xù)看ClassUtils的getFileNameByPackageName方法:

/**
     * 通過指定包名,掃描包下面包含的所有的ClassName
     *
     * @param context     U know
     * @param packageName 包名
     * @return 所有class的集合
     */
    public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
        final Set<String> classNames = new HashSet<>();

        //getSourcePaths方法的主要作用是獲取apk所有dex包的路徑
        List<String> paths = getSourcePaths(context);
        //CountDownLatch保證了只有所有的子線程都運行結(jié)束后才能繼續(xù)運行,構(gòu)造時傳入子線程的數(shù)量,這里傳傳的是dex包的數(shù)量,即一個dex包起一個子線程去加載里面的類
        final CountDownLatch parserCtl = new CountDownLatch(paths.size());

        for (final String path : paths) {
            //這里獲取的線程池的最大線程數(shù)量是cpu個數(shù)+1
            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();
                            //只有符合條件的類才被加入到集合中
                            //包名是com.alibaba.android.arouter.routes下的類
                            //不包含AutowiredProcessor生成的類(不在此包下)
                            if (className.startsWith(packageName)) {
                                classNames.add(className);
                            }
                        }
                    } catch (Throwable ignore) {
                      //...
                    } finally {
                        if (null != dexfile) {
                            try {
                                dexfile.close();
                            } catch (Throwable ignore) {
                            }
                        }
                        //標(biāo)記一個線程結(jié)束
                        parserCtl.countDown();
                    }
                }
            });
        }

        //阻塞,直到所有子線程都跑完
        parserCtl.await();

        return classNames;
    }
  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已經(jīng)支持了MultiDex,就不要去Secondary Folder加載 Classesx.zip了,那里已經(jīng)么有了
      // 通過是否存在sp中的multidex.version是不準(zhǔn)確的,因為從低版本升級上來的用戶,是包含這個sp配置的
      // 通過vm的版本來判斷是否支持MultiDex,vm版本大于等于2.1.0則支持(其中第一位是major版本,2表示ART,1表示Dalvik,第二位表示minor版本,小版本),否則不支持,需要額外去加載。
      // MultiDex支持的判斷以及加載的方法,Arouter參考了Android原生MultiDex包下的相關(guān)代碼,有興趣的同學(xué)可以自己研究下
      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;
    }

拿到符合條件的類集合后,執(zhí)行第二步,按條件將這些信息緩存到Warehouse中,先看看Warehouse類的結(jié)構(gòu):

class Warehouse {   
    //緩存路由分組列表,既作為 ARouter$$Root$$app類中,loadInto的入?yún)?    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    //根據(jù)path緩存某個具體路由節(jié)點
    static Map<String, RouteMeta> routes = new HashMap<>();

    
    static Map<Class, IProvider> providers = new HashMap<>();
    //緩存provider類型的節(jié)點,ARouter$$Providers$$app類中,loadInto的入?yún)?    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    //用于緩存攔截器,ARouter$$Interceptors$$app中l(wèi)oadInto方法的入?yún)?    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

第二步的關(guān)鍵代碼如下:

for (String className : routerMap) {
    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
        // 類名以com.alibaba.android.arouter.routes.ARouter$$Root打頭
        // 就是加載路由分組列表的類
        // 把所有分組緩存在Warehouse的groupsIndex中
        // 只緩存了分組,并沒有加載所有的路由節(jié)點,具體的路由節(jié)點是在真正跳轉(zhuǎn)的時候才會加載該節(jié)點所在的group里所有的路由節(jié)點,也就是會一次性加載group下的所有節(jié)點
        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
        // 類名以com.alibaba.android.arouter.routes.ARouter$$Interceptors打頭
        // 加載所有的攔截器,緩存在Warehouse的interceptorsIndex中
        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
        // 類名以com.alibaba.android.arouter.routes.ARouter$$Providers打頭
        // 加載Providers類型的節(jié)點,緩存在Warehouse的providersIndex中
        // value是以RouteMeta的形式,真正調(diào)用時,會以RouteMeta的信息轉(zhuǎn)換成IProvider類行的服務(wù)接口
        // 不止IProvider類型的節(jié)點,所有的路由節(jié)點都是以RouteMeta為載體的
        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
    }
}

初始化的工作并復(fù)雜,最核心的就是加載了路由分組列表,攔截器,以及provider類型的路由節(jié)點,每個group里的節(jié)點列表是在某個節(jié)點被調(diào)用時,一次性加載其所在group里的所有節(jié)點,然后緩存在Warehouse的routes中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 人真的會老,就像樹葉會枯,水井會干。 命運的存在是讓人明白時間和規(guī)律。 不懂不明白的不代表沒有和不存在。
    敢說真話的妖精閱讀 203評論 0 3
  • 蕾絲花邊絢爛的包裹在她白暫的肩頸,獨特的剪裁凸顯了她纖細的腰身,她小心的將手搭在細滑的綢緞上,捏起一端,踮起腳...
    一個幻想癌的巨蟹座閱讀 1,315評論 0 2
  • Tips 由于WebView的用法實在太多,如果您只是想查詢某個功能的使用——建議Ctrl+F(Commad+F)...
    BugDev閱讀 7,914評論 11 109
  • 2017.11.22 1.意想不到無論從哪個角度考慮問題我們每一個人都是圍著國粹堂這個大集體考慮,一點一滴都去完善...
    吻蝶戀閱讀 197評論 0 0

友情鏈接更多精彩內(nèi)容