目錄
- ARouter整體結構
- ARouter初始化流程的原始碼分析
- ARouter攔截器優先順序的問題
ARouter整體結構
主要的類:
類名 | 類的描述 |
---|---|
ARouter | ARouter facade(門面模式); |
_ARouter | ARouter core (Facade patten) |
LogisticsCenter | LogisticsCenter contain all of the map. |
Postcard | A container that contains the roadmap. |
Warehouse | Storage of route meta and other data. |
ClassUtils | 獲取ARouter自動生成的程式碼檔案 |
ARouter初始化流程的原始碼分析
1. ARouter初始化時序圖
2. ARouter初始化程式碼分析
在Application中初始化程式碼:ARouter.init(application)
;
// ARouter.class
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
// 初始化init()
hasInit = _ARouter.init(application);
if (hasInit) {
// 啟動InterceptorService
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
// _ARouter.class
protected static synchronized boolean init(Application application) {
mContext = application;
// LogisticsCenter的初始化
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
return true;
}
// LogisticsCenter.class
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// 如果是Debug模式,則執行從if程式碼塊;
// 如果是Release模式:通過PackageUtils.isNewVersion(context)判斷當前應用是否是第一次啟動;
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// 獲取arouter-compiler生成的檔案
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// 儲存在sp中,下次啟動應用的時候,直接從sp快取中讀取
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// 儲存當前應用的版本號
PackageUtils.updateVersion(context); // Save new version name when router map update finish.
} else {
// 從sp快取中讀取arouter-compiler生成的檔案
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
// 遍歷arouter-compiler生成的檔案,將他們按照型別分別儲存到Warehouse的對應欄位中
for (String className : routerMap) {
// 類名字首為com.alibaba.android.arouter.routes.ARouter$$Root的檔案
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 將類名字首為com.alibaba.android.arouter.routes.ARouter$$Group的檔案新增進Warehouse.groupsIndex中
((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);
}
}
}
//...程式碼省略...
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
複製程式碼
LogisticsCenter.init()
小結:
- 查詢指定包名(com.alibaba.android.arouter)下的檔案,並將之快取到SP中;
- 按照檔案的字首不同,將他們新增到路由對映表
Warehouse
的groupsIndex、interceptorsIndex、providersIndex
中;
PackageUtils
類:用來管理應用的版本
// PackageUtils .class
// 判斷當前版本是否是新版本(第一次啟動)
public static boolean isNewVersion(Context context) {
//...程式碼省略...
SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
// 從sp檔案中獲取之前儲存的VersionName和VersionCode
if (!versionName.equals(sp.getString(LAST_VERSION_NAME, null)) || versionCode != sp.getInt(LAST_VERSION_CODE, -1)) {
//...程式碼省略...
return true;
} else {
return false;
}
}
// 儲存當前版本的VersionName和VersionCode
public static void updateVersion(Context context) {
if (TextUtils.isEmpty(NEW_VERSION_NAME) && NEW_VERSION_CODE != 0) {
SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
sp.edit()
.putString(LAST_VERSION_NAME, NEW_VERSION_NAME)
.putInt(LAST_VERSION_CODE, NEW_VERSION_CODE)
.apply();
}
}
複製程式碼
ClassUtils
類:獲取com.alibaba.android.arouter.routes
包下 的檔案:
// ClassUtils.class
// 通過指定包名,掃描包下面包含的所有的ClassName
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
// Set型別的資料結構,防止重複
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
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);
}
// 從DexFile中獲取所有的檔案
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
// 匹配所有包名路徑為com.alibaba.android.arouter.routes的檔案
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
//...程式碼省略...
}
}
});
}
//...程式碼省略...
return classNames;// 返回所有包名路徑為com.alibaba.android.arouter.routes的檔案集合
}
複製程式碼
Warehouse
類:路由檔案對映表
class Warehouse {
// Cache route and metas
// LogisticsCenter.init() 中被賦值
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
// LogisticsCenter.completion(postcard) 中被賦值
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
// LogisticsCenter.completion(postcard) 中被賦值
static Map<Class, IProvider> providers = new HashMap<>();
// LogisticsCenter.init() 中被賦值
static Map<String, RouteMeta> providersIndex = new HashMap<>();
/*
* Cache interceptor
* LogisticsCenter.init() 中被賦值;
* 此處攔截器的儲存使用TreeMap,儲存的時候,已經對攔截器的優先順序進行排序
*/
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
// InterceptorServiceImpl.init()中被賦值
static List<IInterceptor> interceptors = new ArrayList<>();
}
複製程式碼
至此,_ARouter.init(application)
的整個流程結束;
下面開始分析_ARouter.afterInit()
程式碼:
// _ARouter.class
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService)
ARouter.getInstance()
.build("/arouter/service/interceptor") //生成一個Postcard物件
//這個navigation()經過多次呼叫之後,
//最終呼叫的是_ARouter.navigation(context, postcard, requestCode, navigationCallback)方法
.navigation();
}
複製程式碼
build("/arouter/service/interceptor")
程式碼分析:
// _ARouter.class
protected Postcard build(String path) {
// navigation(clazz)這種方式是屬於根據型別查詢,而build(path)是根據名稱進行查詢
// 如果應用中沒有實現PathReplaceService這個介面,則pService=null
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
// PathReplaceService可以對所有的路徑進行預處理,然後返回一個新的值
path = pService.forString(path);
}
//extractGroup(path)是獲取分組名稱,預設為path中第一部分;
return build(path, extractGroup(path));
}
// _ARouter.class
protected Postcard build(String path, String group) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
// 返回含有path和group組別的Postcard
return new Postcard(path, group);
}
public interface PathReplaceService extends IProvider {
String forString(String path);
Uri forUri(Uri uri);
}
複製程式碼
navigation()
程式碼分析:
// _ARouter.class
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
// 重點:分析部分在下面
// 1. 將IProvider對映到`Warehouse.providers`中;
// 2. 將新增@Route註解的類對映到`Warehouse.routes`中
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
// ...程式碼省略...
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);
}
// 忽略攔截器(在LogisticsCenter.completion(postcard)中,PROVIDER和FRAGMENT是忽略攔截器的)
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
// interceptorService物件就是通過_ARouter.afterInit()例項化的;
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);
}
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
// _ARouter.class
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// 這裡如果navigation()不傳入Activity作為context,則使用Application作為context
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
// Activity的引數傳遞
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
// 如果在跳轉時,設定了flags,且沒有設定Activity作為context,則下面的startActivity()方法會發生錯誤,因為缺少Activity的Task棧;
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Navigation in main looper.
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 {
// Activity跳轉
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:
//在LogisticsCenter.completion(postcard)中,我們將IProvider物件設定進postcard中,所以可以通過postcard.getProvider()獲取;
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
// Fragment的引數傳遞
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;
}
複製程式碼
LogisticsCenter.completion()
小結:
- 將IProvider對映到
Warehouse.providers
中; - 將新增@Route註解的類對映到
Warehouse.routes
中
// LogisticsCenter.class
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
/*
* 根據path在Warehouse.routes對映表中查詢對應的RouteMeta;
*
* 如果是第一次啟動,要查詢path = "/arouter/service/interceptor",
* 而此時Warehouse.routes裡面是沒有元素的(在LogisticsCenter.init()中,
* 只有`Warehouse`的`groupsIndex、interceptorsIndex、providersIndex` 有資料),因此routeMeta=null
*/
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// 從Warehouse.groupsIndex中取出類名字首為com.alibaba.android.arouter.routes.ARouter$$Group$$group的檔案
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 {
// Load route and cache it into memory, then delete from metas.
try {
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
// 將我們新增@Route註解的類對映到Warehouse.routes中;
iGroupInstance.loadInto(Warehouse.routes);
// 將已經載入過的組從Warehouse.groupsIndex中移除,避免重複新增進Warehouse.routes
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// 這個時候Warehouse.routes已經有值了,所以重新呼叫本方法執行else程式碼塊
completion(postcard); // Reload
}
} else {
// 給postcard賦值
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());//設定routeMeta的Type
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();
// IProvider的子類進行初始化
provider.init(mContext);
// 將IProvider的子類新增進Warehouse.providers中
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
// 將IProvider的實現類儲存在postcard中,因此可以從postcard獲取IProvider的例項物件;
postcard.setProvider(instance);
// greenChannel()會忽略攔截器
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
// greenChannel()會忽略攔截器
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
複製程式碼
到這裡,我們看到了Warehouse.providers、Warehouse.routes
的賦值;那麼Warehouse.interceptors
又是在哪裡賦值的呢?
InterceptorServiceImpl
的初始化:
// InterceptorService是IProvider的子類
public interface InterceptorService extends IProvider {
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
/*
* 因此InterceptorServiceImpl也是IProvider的子類;
* 在LogisticsCenter.completion()中,有provider.init(context)的初始化;
*/
public class InterceptorServiceImpl implements InterceptorService {
// ...程式碼省略...
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
// 具體的攔截器的初始化
iInterceptor.init(context);
// 將攔截器新增到Warehouse.interceptors對映表中
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
// ...程式碼省略...
}
}
});
}
}
複製程式碼
至此,真個路由的初始化已經結束;
ARouter攔截器優先順序的問題
class Warehouse {
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
}
public class UniqueKeyTreeMap<K, V> extends TreeMap<K, V> {
private String tipText;
public UniqueKeyTreeMap(String exceptionText) {
super();
tipText = exceptionText;
}
@Override
public V put(K key, V value) {
/*
* 將攔截器新增進UniqueKeyTreeMap時,以優先順序priority作為鍵,
* 所以應用中攔截器的優先順序不能重複,否則會丟擲異常;
*/
if (containsKey(key)) {
throw new RuntimeException(String.format(tipText, key));
} else {
return super.put(key, value);
}
}
}
複製程式碼
小結:
將攔截器新增進UniqueKeyTreeMap時,以優先順序priority作為鍵,所以應用中攔截器的
優先順序不能重複
,否則會丟擲異常;
其他
Activity跳轉的原理:
- 從DexFile檔案中查詢到被@Route註解修飾的類;
- 儲存在Warehouse中;
- 在跳轉的時候,將所需查詢的Class賦值給Postcard物件;
- 最後使用的時候從postcard的Destination中獲取需跳轉的Class物件;
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:
// Build intent
// 這裡從postcard的Destination中獲取需跳轉的Class物件
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// ...程式碼省略...
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
//...程式碼省略...
}
return null;
}
// 另一種方式:通過類名直接跳轉
try {
// GoodsQueryActivity.class為KitBundle元件中的頁面,當前元件為MainBundle元件;通過類名查詢到對應的Class,也能進行頁面跳轉;
Class clazz = Class.forName("com.elson.kitbundle.ui.GoodsQueryActivity");
Intent intent = new Intent(this, clazz);
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
複製程式碼