Arouter
在编译器的主要工作就是生成中间件的代码,在gradle中加入Arouter的依赖后在编译的时候就会在对应的module下添加com.alibaba.android.arouter.routes
目录,这个目录中主要存放Arouter生成的文件,比如
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map> routes) {
//routes是一个map类型,key值就是path路径中的group,value值就是下面的那个文件
routes.put("app", ARouter$$Group$$app.class);
}
}
public class ARouter$$Group$$app implements IRouteGroup {
@Override
public void loadInto(Map atlas) {
//key是通过@Route配置的路径,value是保存了启动的组件类型以及对应的文件
atlas.put("/app/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/app/loginactivity", "app", new java.util.HashMap(){{put("path", 8); }}, -1, -2147483648));
atlas.put("/app/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/mainactivity", "app", null, -1, -2147483648));
atlas.put("/app/SecondActivity", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/app/secondactivity", "app", null, -1, -2147483648));
atlas.put("/app/ThirdActivity", RouteMeta.build(RouteType.ACTIVITY, ThirdActivity.class, "/app/thirdactivity", "app", new java.util.HashMap(){{put("name", 8); put("hero", 0); put("age", 3); }}, -1, 0));
}
}
/**
* For versions of 'compiler' greater than 1.0.7
*
* @param type type 类型(Activity、Service等)
* @param destination destination 具体类(MainActivity.class)
* @param path path 路径(app/MainActivity)
* @param group group app
* @param paramsType paramsType null
* @param priority priority
* @param extra extra
* @return this
*/
public static RouteMeta build(RouteType type, Class> destination, String path, String group, Map paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
}
因为编译生成的类的前缀都是 Arouter,所以moudle下的完整包名就是com.alibaba.android.arouter.routes.Arouter
ARouter.init(this);
init
最终是调用到了这里//_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;
}
init
方法中又调用了LogisticsCenter
//LogisticsCenter#init
//第一个参数context是Application它来自Arouter.init
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//billy.qi modified at 2017-12-06
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set routerMap;
// 这里做了是否是debug或者是否是新版本的判断,如果结果为true就需要重新获取包下面所有的class
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 这里会获取com.alibaba.android.arouter.routes目录下的所有class文件
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
//获取到class后将其存储到SharedPreferences中
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
//router保存完成时还要保存最新的版本
PackageUtils.updateVersion(context);
} else {
logger.info(TAG, "Load router map from cache.");
//不是新的版本或者没有debug时可直接从SharedPreference中获取所有的class
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
//遍历获取到的class文件名,然后对不同类名的前缀做对比后通过loadInto注入到Warehouse
for (String className : routerMap) {
//com.alibaba.android.arouter.routes.Arouter$$Root
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((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
((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$$Provider
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
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() + "]");
}
}
LogisticsCenter
方法的流程如下:
ClassUtils.getFileNameByPackageName
方法重新获取className然后加入到SharedPreference
中缓存SharedPreference
获取loadinto
方法注入到Warehouse
中//ClassUtils#getFileNameByPackageName
public static Set getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set classNames = new HashSet<>();
//获取所有dex文件的路径
List paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
//遍历获取到的dex文件的路径
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//如果是.zip结尾的路径就用loadDex加载,如果创建新的DexFile就会报出权限错误
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
//创建新的DexFile
dexfile = new DexFile(path);
}
Enumeration dexEntries = dexfile.entries();
//遍历dexFile下面的元素
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
//将开头为"com.alibaba.android.arouter.routes"的className添加到classNames集合中
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();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
获取classNames的流程如下:
com.alibaba.android.arouter.routes
开头则添加到ClassNames
集合中//ClassUtils#getSourcePaths
public static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List sourcePaths = new ArrayList<>();
//添加默认的apk路径
sourcePaths.add(applicationInfo.sourceDir);
//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配置的
//如果已经支持了MutilDex
if (!isVMMultidexCapable()) {
//获取所有dex文件的数量
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文件路径的流程如下:
MutilDex
sourcePaths
集合中class Warehouse {
// Cache route and metas
static Map> groupsIndex = new HashMap<>();
static Map routes = new HashMap<>();
// Cache provider
static Map providers = new HashMap<>();
static Map providersIndex = new HashMap<>();
// Cache interceptor
static Map> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
Warehouse
中包含三种缓存类型
Arouter在运行时的完整工作流程如下:
Application
中初始化ClassUtils.getFileNameByPackageNam()
获取所有com.alibaba.android.arouter.route
目录下的dex文件路径loadinto
方法根据不同前缀注入到Warehouse
对应的成员变量中com.alibaba.android.arouter.routes.Arouter$$Root.Arouter$$Root$$app
ARouter.getInstance().build(ActivityConsts.ACTIVITY_URL_THIRD)
.navigation(this, new CustomNavigationCallback()));
build
看起//_Arouter#build
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
//_Arouter#build
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
//extractGroup获取到group,就是代码中的【app】
return build(path, extractGroup(path));
}
}
//_Arouter#extractGroup
private String extractGroup(String path) {
//校验path格式是否正确
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
//通过截取path获取到group
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
//_Arouter#build
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
//创建一个Postcard
return new Postcard(path, group);
}
}
public Postcard(String path, String group) {
this(path, group, null, null);
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
//保存路径
setPath(path);
//保存group,就是【app】
setGroup(group);
setUri(uri);
//创建了个Bundle()
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
Arouter.getInstance().build()
的流程用一句话总结就是,创建了一个Postcard
保存了path
和group
navigation的源码如下
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
//核心1
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) { // Show friendly tips for user.
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
if (null != callback) {
callback.onLost(postcard);
} else { // No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
//判断是否调用拦截器
//要在异步线程中调用,否则可能会因为拦截器耗时过长出现ANR
if (!postcard.isGreenChannel()) {
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
//核心2
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
//LogisticsCenter#completion
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//先从Warehouse.routes获取RouteMeta,上面的代码中只用到了Warehouse.groupsIndex,所以肯定是null
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//这里通过group获取具体的class,而这个class就是前面编译时缓存进去的
Class extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
//加载路由并缓存到内存中,然后将元数据删除,这里的元数据指的是group
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//通过反射将其存入到Warehouse中
iGroupInstance.loadInto(Warehouse.routes);
//移除group,group的意义就是用来获取route的
Warehouse.groupsIndex.remove(postcard.getGroup());
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// 又调了自己,回到这个函数头重新看
completion(postcard);
}
} else {
//第二次进入时到这里
//配置一些参数
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map resultMap = TextUtils.splitQueryParameters(rawUri);
Map paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
//根据类型执行逻辑
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must be implememt IProvider
Class extends IProvider> providerMeta = (Class extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
completion
方法运行了两次:
Warehouse.groupIndex
中取出之前保存的数据,然后再通过loadinto
把相关数据注入到Warehouse.routes
中Postcard
中的属性赋值,type、Destination
等经过completion
方法后Warehouse.route
中便有了数据,并且参数postcard
也有了type
和Destinatio
,就该执行第二个核心方法了
//_Arouter#_navigation
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
// Activity类型就是创建了Intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// 切换到UI线程去启动Activity,Activity启动了
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (requestCode > 0) { // Need start for result
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
_navigation
方法中首先根据postcard.type
获取到具体类型,然后根据不同类型做相关处理,代码中我们使用的是Activity
因此从源码中可知在Arouter中Activity的开启也是基于Intent
完成的