初始化主要是指如何將APT生成的路由類,攔截器類和服務提供類註冊到內存中,供運行時使用。java
ARuter提供了兩種方式,分別是gradle插件自動註冊和掃描dex文件。android
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
代碼插入流程:ide
調用RegisterCodeGenerator.insertInitCodeTo()方法進行代碼插入到LogisticsCenter的class文件中gradle
經過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)
}
}
複製代碼
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代碼掃描和插入,實現了路由和服務的自動註冊。線程
若是沒有使用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中。