ARouter 是阿里推出的一款頁面路由框架。因爲項目中採用了組件化架構進行開發,經過 ARouter 實現了頁面的跳轉,以前看它的源碼時忘了寫筆記,所以今天來從新對它的源碼進行一次分析。android
(順手留下GitHub連接,須要獲取相關面試或者面試寶典核心筆記PDF等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MSgit
本篇源碼解析基於 ARouter 1.2.4github
ARouter 在使用前須要經過調用 Arouter.init
方法並傳入 Application
進行初始化:面試
/** * Init, it must be call before used router. */ public static void init(Application application) { if (!hasInit) { logger = _ARouter.logger; _ARouter.logger.info(Consts.TAG, "ARouter init start."); hasInit = _ARouter.init(application); if (hasInit) { _ARouter.afterInit(); } _ARouter.logger.info(Consts.TAG, "ARouter init over."); } }
這裏調用到了 _ARouter.init
,這個 _ARouter
類纔是 ARouter 的核心類:緩存
protected static synchronized boolean init(Application application) { mContext = application; LogisticsCenter.init(mContext, executor); logger.info(Consts.TAG, "ARouter init success!"); hasInit = true; // It's not a good idea. // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback()); } return true; }
這裏實際上調用到了 LogisticsCenter.init
:架構
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { mContext = context; executor = tpe; try { long startInit = System.currentTimeMillis(); Set<String> routerMap; // 獲取存儲 ClassName 集合的 routerMap(debug 模式下每次都會拿最新的) if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { logger.info(TAG, "Run with debug mode or new install, rebuild router map."); // 根據指定的 packageName 獲取 package 下的全部 ClassName routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE); if (!routerMap.isEmpty()) { // 存入 SP 緩存 context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply(); } } else { logger.info(TAG, "Load router map from cache."); // release 模式下,已經緩存了 ClassName 列表 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(); // 遍歷 ClassName for (String className : routerMap) { if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { // 發現是 Root,加載類構建對象後經過 loadInto 加載進 Warehouse.groupsIndex ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) { // 發現是 Interceptor,加載類構建對象後經過 loadInto 加載進 Warehouse.interceptorsIndex ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) { // 發現是 ProviderGroup,加載類構建對象後經過 loadInto 加載進 Warehouse.providersIndex ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } } // ... } catch (Exception e) { throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]"); } }
1.獲取
com.alibaba.android.arouter.routes
下存儲ClassName
的集合routerMap
。
2.若爲 debug 模式或以前沒有解析過routerMap
,則經過ClassUtils.getFileNameByPackageName
方法對指定 package 下的全部ClassName
進行解析並存入 SP。
3.若並不是 debug 模式,而且以前已經解析過,則直接從 SP 中取出。(debug 每次都須要更新,由於類會隨着代碼的修改而變更)
4.遍歷routerMap
中的ClassName
。併發
- 若是是
RouteRoot
,則加載類構建對象後經過loadInto
加載進Warehouse.groupsIndex
。- 若是是
InterceptorGroup
,則加載類構建對象後經過loadInto
加載進Warehouse.interceptorsIndex
。- 若是是
ProviderGroup
,則加載類構建對象後經過loadInto 加載進
Warehouse.providersIndex`。
ClassName
咱們先看看 ClassUtils.getFileNameByPackageName
是如何對指定 package 下的 ClassName
集合進行解析的:app
public static Set<String> getFileNameByPackageName(Context context, final String packageName) { final Set<String> classNames = new HashSet<>(); // 經過 getSourcePaths 方法獲取 dex 文件 path 集合 List<String> paths = getSourcePaths(context); // 經過 CountDownLatch 對 path 的遍歷處理進行控制 final CountDownLatch parserCtl = new CountDownLatch(paths.size()); // 遍歷 path,經過 DefaultPoolExecutor 併發對 path 進行處理 for (final String path : paths) { DefaultPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { // 加載 path 對應的 dex 文件 DexFile dexfile = null; try { if (path.endsWith(EXTRACTED_SUFFIX)) { // zip 結尾經過 DexFile.loadDex 進行加載 dexfile = DexFile.loadDex(path, path + ".tmp", 0); } else { // 不然經過 new DexFile 加載 dexfile = new DexFile(path); } // 遍歷 dex 中的 Entry Enumeration<String> dexEntries = dexfile.entries(); while (dexEntries.hasMoreElements()) { // 若是是對應的 package 下的類,則添加其 className 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(); } } }); } // 全部 path 處理完成後,繼續向下走 parserCtl.await(); Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">"); return classNames; }
這裏的步驟比較簡單,主要是以下的步驟:框架
1.經過
getSourcePaths
方法獲取dex
文件的 path 集合。
2.建立了一個CountDownLatch
控制dex
文件的並行處理,以加快速度。
3.遍歷 path 列表,經過DefaultPoolExecutor
對 path 並行處理。
4.加載 path 對應的dex
文件,並對其中的 Entry 進行遍歷,若發現了對應 package 下的ClassName
,將其加入結果集合。
5.全部dex
處理完成後,返回結果less
關於 getSourcePaths
如何獲取到的 dex
集合這裏就不糾結了,由於咱們的關注點不在這裏。
Warehouse 實際上就是倉庫的意思,它存放了 ARouter 自動生成的類(RouteRoot
、InterceptorGroup
、ProviderGroup
)的信息。
咱們先看看 Warehouse 類到底是怎樣的:
class Warehouse { // 保存 RouteGroup 對應的 class 以及 RouteMeta static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); static Map<String, RouteMeta> routes = new HashMap<>(); // 保存 Provider 以及 RouteMeta static Map<Class, IProvider> providers = new HashMap<>(); static Map<String, RouteMeta> providersIndex = new HashMap<>(); // 保存 Interceptor 對應的 class 以及 Inteceptor 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 就是一個純粹用來存放信息的倉庫類,它的數據的其實是經過上面的幾個自動生成的類在 loadInto
中對 Warehouse 主動填入數據實現的。
例如咱們打開一個自動生成的 IRouteRoot
的實現類:
public class ARouter$$Root$$homework implements IRouteRoot { @Override public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) { routes.put("homework", ARouter$$Group$$homework.class); } }
能夠看到,它在 groupsIndex
中對這個 RouteRoot
中的 IRouteGroup
進行了註冊,也就是向 groupIndex
中註冊了 Route Group 對應的 IRouteGroup
類。其餘類也是同樣,經過自動生成的代碼將數據填入 Map 或 List 中。
能夠發現,初始化過程主要完成了對自動生成的路由相關類 RouteRoot
、Interceptor
、ProviderGroup
的加載,對它們經過反射構造後將信息加載進了 Warehouse 類中。
下面咱們看看路由的跳轉是如何實現的,咱們先看到 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); } return build(path, extractGroup(path)); } }
它首先經過 ARouter.navigation
獲取到了 PathReplaceService
,它須要用戶進行實現,若沒有實現會返回 null,如有實現則調用了它的 forString
方法傳入了用戶的 Route Path 進行路徑的預處理。
最後轉調到了 build(path, group)
,group 經過 extractGroup
獲得:
private String extractGroup(String 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 { 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; } }
extractGroup
實際上就是對字符串處理,取出 Route Group 的名稱部分。
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); } return new Postcard(path, group); } }
build(path, group)
方法一樣也會嘗試獲取到 PathReplaceService
並對 path 進行預處理。以後經過 path 與 group 構建了一個 Postcard
類:
public Postcard(String path, String group) { this(path, group, null, null); } public Postcard(String path, String group, Uri uri, Bundle bundle) { setPath(path); setGroup(group); setUri(uri); this.mBundle = (null == bundle ? new Bundle() : bundle); }
這裏最終調用到了 PostCard(path, group, uri, bundle)
,這裏只是進行了一些參數的設置。
以後,若是咱們調用 withInt
、withDouble
等方法,就能夠進行參數的設置。例如 withInt
方法:
public Postcard withInt(@Nullable String key, int value) { mBundle.putInt(key, value); return this; }
它實際上就是在對 Bundle 中設置對應的 key、value。
最後咱們經過 navigation
便可實現最後的跳轉:
public Object navigation() { return navigation(null); } public Object navigation(Context context) { return navigation(context, null); } public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); } public void navigation(Activity mContext, int requestCode) { navigation(mContext, requestCode, null); } public void navigation(Activity mContext, int requestCode, NavigationCallback callback) { ARouter.getInstance().navigation(mContext, this, requestCode, callback); }
經過如上的 navigation 能夠看到,實際上它們都是最終調用到 ARouter.navigation
方法,在沒有傳入 Context
時會使用 Application
初始化的 Context
,而且能夠經過 NavigationCallback
對 navigation
的過程進行監聽。
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) { return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback); }
ARouter
仍然只是將請求轉發到了 _ARouter
:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { try { // 經過 LogisticsCenter.completion 對 postcard 進行補全 LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { // ... } if (null != callback) { callback.onFound(postcard); } // 若是設置了 greenChannel,會跳過全部攔截器的執行 if (!postcard.isGreenChannel()) { // 沒有跳過攔截器,對 postcard 的全部攔截器進行執行 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 { return _navigation(context, postcard, requestCode, callback); } return null; }
上面的代碼主要有如下步驟:
1.經過
LogisticsCenter.completion
對 postcard 進行補全。
2.若是postcard
沒有設置greenChannel
,則對postcard
的攔截器進行執行,執行完成後調用_navigation
方法真正實現跳轉。
3.若是postcard
設置了greenChannel
,則直接跳過全部攔截器,調用_navigation
方法真正實現跳轉。
咱們看看 LogisticsCenter.completion
是如何實現 postcard
的補全的:
public synchronized static void completion(Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!"); } // 經過 Warehouse.routes.get 嘗試獲取 RouteMeta RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if (null == routeMeta) { // 若 routeMeta 爲 null,多是並不存在,或是尚未加載進來 // 嘗試獲取 postcard 的 RouteGroup 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 { // ... // 若是找到了對應的 RouteGroup,則將其加載進來並從新調用 completion 進行補全 IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance(); iGroupInstance.loadInto(Warehouse.routes); Warehouse.groupsIndex.remove(postcard.getGroup()); // ... completion(postcard); // Reload } } else { // 若是找到了對應的 routeMeta,將它的信息設置進 postcard 中 postcard.setDestination(routeMeta.getDestination()); postcard.setType(routeMeta.getType()); postcard.setPriority(routeMeta.getPriority()); postcard.setExtra(routeMeta.getExtra()); Uri rawUri = postcard.getUri(); // 將 uri 中的參數設置進 bundle 中 if (null != rawUri) { 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()); } // 對於 provider 和 fragment,進行特殊處理 switch (routeMeta.getType()) { case PROVIDER: // 若是是一個 provider,嘗試從 Warehouse 中查找它的類並構造對象,而後將其設置到 provider 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); // provider 和 fragment 都會跳過攔截器 postcard.greenChannel(); break; case FRAGMENT: // provider 和 fragment 都會跳過攔截器 postcard.greenChannel(); default: break; } } }
這個方法主要完成了對 postcard
的信息與 Warehouse 的信息進行結合,以補全 postcard
的信息,它的步驟以下:
1.經過
Warehouse.routes.get
根據 path 嘗試獲取RouteMeta
對象。
2.若獲取不到RouteMeta
對象,多是不存在或是尚未進行加載(第一次都未加載),嘗試獲取RouteGroup
調用其loadInto
方法將RouteMeta
加載進 Warehouse,最後調用 completion 從新嘗試補全 。
3.將RouteMeta
的信息設置到 postcard 中,其中會將rawUri
的參數設置進 Bundle。
4.對於Provider
和Fragment
特殊處理,其中Provider
會從Warehouse
中加載並構造它的對象,而後設置到postcard
。Provider
和Fragment
都會跳過攔截器。
RouteGroup
的 loadInto
仍然是自動生成的,例以下面就是一些自動生成的代碼:
public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, "/homework/commit", "homework", null, -1, -2147483648)); // ... }
它包括了咱們補全所須要的如 Destination、Class、path 等信息,在生成代碼時自動根據註解進行生成。
咱們看看 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); } // 切換到主線程,根據是否須要 result 調用不一樣的 startActivity 方法 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: // provider 直接返回對應的 provider return postcard.getProvider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: // 對於 broadcast、contentprovider、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; }
能夠發現,它會根據 postcard
的 type
來分別處理:
postcard
中的參數設置進去,以後會根據是否須要 result 調用不一樣的 startActivity
方法。Provider
,直接返回其對應的 provider 對象。Broadcast
、ContentProvider
、Fragment
,反射構造對象後,將參數設置進去並返回。能夠發現 ARouter
的初始化和路由跳轉的總體邏輯仍是不難的,實際上就是對 Activity
、Fragment
的調轉過程進行了包裝。
ARouter 除了能夠經過 ARouter.getInstance().build().navigation()
這樣的方式實現頁面跳轉以外,還能夠經過 ARouter.getInstance().navigation(XXService.class)
這樣的方式實現跨越組件的服務獲取,咱們看看它是如何實現的:
public <T> T navigation(Class<? extends T> service) { return _ARouter.getInstance().navigation(service); }
仍然跳轉到了_ARouter
中去實現:
protected <T> T navigation(Class<? extends T> service) { try { Postcard postcard = LogisticsCenter.buildProvider(service.getName()); // Compatible 1.0.5 compiler sdk. // Earlier versions did not use the fully qualified name to get the service if (null == postcard) { // No service, or this service in old version. postcard = LogisticsCenter.buildProvider(service.getSimpleName()); } if (null == postcard) { return null; } LogisticsCenter.completion(postcard); return (T) postcard.getProvider(); } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage()); return null; } }
這裏首先經過 LogisticsCenter.buildProvider
傳入service.class
的 name 構建出了一個 postcard。
而在 ARouter 老版本中,並非經過這樣一個完整的 name 來獲取 Service 的,而是經過 simpleName,下面爲了兼容老版本,在獲取不到時會嘗試用老版本的方式從新構建一次。
以後會經過 LogisticsCenter.completion
對 postcard 進行補全,最後經過 postcard.Provider
獲取對應的 Provider。
除了 buildProvider
以外,其餘方法咱們已經在前面進行過度析,就再也不贅述了:
public static Postcard buildProvider(String serviceName) { RouteMeta meta = Warehouse.providersIndex.get(serviceName); if (null == meta) { return null; } else { return new Postcard(meta.getPath(), meta.getGroup()); } }
這裏實際上很是簡單,就是經過 Warehouse 中已經初始化的 providersIndex
根據 serviceName
獲取對應的 RouteMeta
,以後根據 RouteMeta
的 path 和 group 返回對應的 Postcard
經過前面的分析,能夠發現 ARouter 中存在一套攔截器機制,在 completion 的過程當中對攔截器進行了執行,讓咱們看看它的攔截器機制的實現。
咱們先看到 IInterceptor
接口:
public interface IInterceptor extends IProvider { /** * The operation of this interceptor. * * @param postcard meta * @param callback cb */ void process(Postcard postcard, InterceptorCallback callback); }
攔截器中主要經過 process 方法完成執行過程,能夠在其中對 postcard 進行處理。而攔截器的執行咱們知道,是經過InterceptorServiceImpl.doInterceptions
實現的:
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) { checkInterceptorsInitStatus(); if (!interceptorHasInit) { callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time.")); return; } LogisticsCenter.executor.execute(new Runnable() { @Override public void run() { CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size()); try { _excute(0, interceptorCounter, postcard); interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS); if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings. callback.onInterrupt(new HandlerException("The interceptor processing timed out.")); } else if (null != postcard.getTag()) { // Maybe some exception in the tag. callback.onInterrupt(new HandlerException(postcard.getTag().toString())); } else { callback.onContinue(postcard); } } catch (Exception e) { callback.onInterrupt(e); } } } else { callback.onContinue(postcard); }
這裏的執行經過一個 Executor 執行,它首先構造了一個值爲 interceptors
個數的 CountDownLatch
,以後經過 _execute 方法進行執行:
那麼 ARouter
是如何自動生成 RouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
的子類的呢?
實際上 ARouter 是經過 AnnotationProcessor 配合 AutoService 實現的,而對於類的生成主要是經過 JavaPoet 實現了對 Java 文件的編寫,關於 JavaPoet 的具體使用能夠看到其 GitHub 主頁https://github.com/xiangjiana/Android-MS
因爲註解處理部分的代碼大部分就是獲取註解的屬性,並結合 JavaPoet
生成每一個 Element 對應的 Java 代碼,這塊的代碼比較多且並不複雜,這裏就不帶你們去看這部分的源碼了,有興趣的讀者能夠看看 arouter-complier
包下的具體實現。
ARouter 的核心流程主要分爲三部分:
編譯期註解處理
經過 AnnotationProcessor
配合 JavaPoet
實現了編譯期根據註解對 RouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
等類的代碼進行生成,在這些類中完成了對 Warehouse 中裝載註解相關信息的工做。
經過ARouter.init
,能夠對ARouter
進行初始化,它主要分爲兩個步驟:
1.遍歷
Apk
的dex
文件,查找存放自動生成類的包下的類的ClassName
集合。其中爲了加快查找速度,經過一個線程池進行了異步查找,並經過CountDownLatch
來等待全部異步查找任務的結束。這個查找過程在非 debug 模式下是有緩存的,由於 release 的 Apk 其自動生成的類的信息必然不會變化
2.根據ClassName
的類型,分別構建RouteRoot
、InterceptorGroup
、ProviderGroup
的對象並調用了其loadInto
方法將這些 Group 的信息裝載進 Warehouse,這個過程並不會將具體的RouteMeta
裝載。這些 Group 中主要包含了一些其對應的下一級的信息(如RouteGroup
的 Class 對象等),以後就只須要取出下一級的信息並從中裝載,再也不須要遍歷 dex 文件。
路由的過程,主要分爲如下幾步:
1. 經過
ARouter
中的 build(path) 方法構建出一個 Postcard,或直接經過其navigate(serviceClass)
方法構建一個 Postcard。
2. 經過對 Postcard 中提供的一系列方法對此次路由進行配置,包括攜帶的參數,是否跳過攔截器等等。
3.經過 navigation 方法完成路由的跳轉,它的步驟以下:
- a.經過
LogisticsCenter.completion
方法根據 Postcard 的信息結合 Warehouse 中加載的信息對 Postcard 的 Destination、Type 等信息進行補全,這個過程當中會實現對RouteMeta
信息的裝載,而且對於未跳過攔截器的類會逐個調用攔截器進行攔截器處理。- b.根據補全後 Postcard 的具體類型,調用對應的方法進行路由的過程(如對於 Activity 調用
startActivity
,對於 Fragment 構建對象並調用setArgument
)。4.將 navigation 的結果返回(Activity 返回的就是 null)
(順手留下GitHub連接,須要獲取相關面試或者面試寶典核心筆記PDF等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MS