敞开成长之旅!这是我参加「日新计划 12 月更文应战」的第23天,点击检查活动详情
这篇内容是曾经在CSDN更新过的,由于CSDN用的少了就干脆转移到上来,内容或许会有一些收支,还请各位看官指正。
1.编译
Arouter
在编译器的首要作业便是生成中间件的代码,在gradle中加入Arouter的依靠后在编译的时分就会在对应的module下增加com.alibaba.android.arouter.routes
目录,这个目录中首要寄存Arouter生成的文件,比方
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> 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<String, RouteMeta> atlas) {
//key是通过@Route装备的途径,value是保存了发动的组件类型以及对应的文件
atlas.put("/app/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/app/loginactivity", "app", new java.util.HashMap<String, Integer>(){{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<String, Integer>(){{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<String, Integer> paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
}
由于编译生成的类的前缀都是 Arouter,所以moudle下的完好包名便是com.alibaba.android.arouter.routes.Arouter
2.运行
- 运行时首要作业便是注入,从Application中的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<String> 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<String>()));
}
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
办法的流程如下:
-
- 判别是否是新版别或者是debug模式
- 假如判别的条件成立就调用
ClassUtils.getFileNameByPackageName
办法从头获取className然后加入到SharedPreference
中缓存 - 假如条件不成立就直接从
SharedPreference
获取 - 遍历获取到的ClassName,然后依据不同类名的前缀做比照后通过
loadinto
办法注入到Warehouse
中
//ClassUtils#getFileNameByPackageName
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
//获取一切dex文件的途径
List<String> 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<String> 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的流程如下:
-
- 获取dexFile途径
- 遍历dexFile途径以及下面的元素
- 假如是以
com.alibaba.android.arouter.routes
开头则增加到ClassNames
集合中
//ClassUtils#getSourcePaths
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<>();
//增加默认的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文件途径的流程如下:
-
- 增加apk默认途径
- 是否支撑
MutilDex
-
-
- 假如支撑则先获取dex文件的总数
- 然后开始遍历获取途径
- 增加到
sourcePaths
集合中
-
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
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();
}
}
-
Warehouse
中包括三种缓存类型 - Arouter在运行时的完好作业流程如下:
-
- 在
Application
中初始化 - 初始化时中先调用
ClassUtils.getFileNameByPackageNam()
获取一切com.alibaba.android.arouter.route
目录下的dex文件途径 - 遍历dex文件途径获取一切的class文件的完好类名
- 遍历获取到的class,获取指定的前缀,调用
loadinto
办法依据不同前缀注入到Warehouse
对应的成员变量中 - 这其中就包括Arouter在编译时生成的
com.alibaba.android.arouter.routes.Arouter$$Root.Arouter$$Root$$app
- 在
3.调用
- 调用时的作业首要是获取,先看源码
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;
}
- 中心1
//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 {
//第2次进入时到这儿
//装备一些参数
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<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> 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
中 - 第2次便是给
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
完结的