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中。