转载请注明出处:http://blog.csdn.net/crazy1235/article/details/77337691
上一篇介绍了ARouter的Compiler SDK : 阿里路由框架–ARouter 源码解析之Compiler
在编译阶段会根据注解生成相应的 java 类文件。
接下来分析一下 arouter-api SDK
先来看看目录结构:
ARouter.openLog();
ARouter.openDebug();
ARouter.init(getApplication());
ARouter.java
public static synchronized void openLog() {
_ARouter.openLog();
}
_ARouter.java
static ILogger logger = new DefaultLogger(Consts.TAG);
ILogger是一个接口,定义了一些日志输出函数
public class DefaultLogger implements ILogger
static synchronized void openLog() {
logger.showLog(true);
logger.info(Consts.TAG, "ARouter openLog");
}
ARouter中的函数基本都是通过调用_ARouter类中的函数来实现的,这里应用了一种 外观模式
public static synchronized void openDebug() {
_ARouter.openDebug();
}
// //设置debug模式
static synchronized void openDebug() {
debuggable = true;
logger.info(Consts.TAG, "ARouter openDebug");
}
初始化ARouter
private volatile static boolean hasInit = false;
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
// log
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
// 调用_ARouter的init函数
hasInit = _ARouter.init(application);
// 调用afterInit()
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
_ARouter.java
protected static synchronized boolean init(Application application) {
mContext = application;
// 初始化 “处理中心”
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
return true;
}
// 线程池执行对象
private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
// compile编译阶段生成的类文件都在此目录下
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
/**
*
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
// 根据报名加载下面所有的类文件(也就是ARouter编译生成的文件)
List classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// 遍历文件
for (String className : classFileNames) {
// 如果是 文件名是 com.alibaba.android.arouter.routes.ARouter$$Root 开头的文件,表示是一个Root类型的文件。
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 创建Root类对象,并放入到WareHouse这个仓库中
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
// 如果文件名以 com.alibaba.android.arouter.routes.ARouter$$Interceptors 开头,则表示拦截器类
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 同样将拦截器添加到仓库中。
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
// 如果文件名以 com.alibaba.android.arouter.routes.ARouter$$Providers 开头,则表示是服务类。
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 也是实例化添加到仓库中。
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
Warehouse 类是一个仓库,用来存储路由信息以及 provider , interceptor 等信息。
接下来重点看怎么获取ARouter compile生成的类文件
ARouter有如下特性:
支持InstantRun
支持MultiDex(Google方案)
所以下面这个函数就显得很重要!!!
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
public static List getFileNameByPackageName(Context context, String packageName) throws PackageManager.NameNotFoundException, IOException {
List classNames = new ArrayList<>();
// 先去得到所有的dex文件,然后进行遍历
for (String path : getSourcePaths(context)) {
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 dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.contains(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) {
}
}
}
}
Log.d("ARouter", "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
// /data/app/com.alibaba.android.arouter.demo-2/base.apk
File sourceApk = new File(applicationInfo.sourceDir);
List 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;
}
加载InstantRun 模式下的dex文件
/**
* Get instant run dex path, used to catch the branch usingApkSplits=false.
*/
private static List tryLoadInstantRunDexFile(ApplicationInfo applicationInfo) {
List instantRunSourcePaths = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && null != applicationInfo.splitSourceDirs) {
// add the splite apk, normally for InstantRun, and newest version.
instantRunSourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
Log.d("ARouter", "Found InstantRun support");
} else {
try {
// This man is reflection from Google instant run sdk, he will tell me where the dex files go.
Class pathsByInstantRun = Class.forName("com.android.tools.fd.runtime.Paths");
Method getDexFileDirectory = pathsByInstantRun.getMethod("getDexFileDirectory", String.class);
String instantRunDexPath = (String) getDexFileDirectory.invoke(null, applicationInfo.packageName);
File instantRunFilePath = new File(instantRunDexPath);
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] dexFile = instantRunFilePath.listFiles();
for (File file : dexFile) {
if (null != file && file.exists() && file.isFile() && file.getName().endsWith(".dex")) {
instantRunSourcePaths.add(file.getAbsolutePath());
}
}
Log.d("ARouter", "Found InstantRun support");
}
} catch (Exception e) {
Log.e("ARouter", "InstantRun support error, " + e.getMessage());
}
}
return instantRunSourcePaths;
}
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
http://blog.csdn.net/fei20121106/article/details/73743235