Arouter原理分析

在app的開發中,頁面之間的相互跳轉是最基本經常使用的功能。在Android中的跳轉通常經過顯式intent和隱式intent兩種方式實現的,而Android的原生跳轉方式會存在一些缺點:android

顯式intent的實現方式,由於會存在直接的類依賴的問題,致使耦合嚴重; 隱式intent的實現方式,則會出現規則集中式管理,致使協做變得困難; 可配置性較差,通常而言配置規則都是在Manifest中的,這就致使了擴展性較差; 跳轉過程沒法控制,一旦使用了StartActivity()就沒法插手其中任何環節了,只能交給系統管理; 特別是當使用多組件化開發時,使用原生的路由方式很難實現徹底解耦;緩存

而阿里的ARouter路由框架具備解耦、簡單易用、支持多模塊項目、定製性較強、支持攔截邏輯等諸多優勢,很好的解決了上述的問題。app

Arouter原理

  1. ARouter在編譯時會生成module下生成這些文件框架

    public class ARouter$$Root$$app implements IRouteRoot {
       @Override
       public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
         routes.put("app", ARouter$$Group$$app.class);
       }
     }
    
     public class ARouter$$Group$$app implements IRouteGroup {
       @Override
       public void loadInto(Map<String, RouteMeta> atlas) {
         atlas.put("/app/main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/main", "app", null, -1, -2147483648));
         atlas.put("/app/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/app/second", "app", null, -1, -2147483648));
       }
     }
    
     public class ARouter$$Providers$$app implements IProviderGroup {
       @Override
       public void loadInto(Map<String, RouteMeta> providers) {
       }
     }
    複製代碼
  2. 接着看一下初始化方法less

    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.afterInit方法async

  3. 跟進_ARouter.init方法ide

    protected static synchronized boolean init(Application application) {
         mContext = application;
         LogisticsCenter.init(mContext, executor);
         logger.info(Consts.TAG, "ARouter init success!");
         hasInit = true;
         mHandler = new Handler(Looper.getMainLooper());
    
         return true;
     }
    複製代碼

    最主要的是ogisticsCenter.init(mContext, executor)這一句,其餘的都是進行基本的賦值oop

    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
         mContext = context;
         executor = tpe;
         try {
             long startInit = System.currentTimeMillis();
             //先經過插件加載
             loadRouterMap();
             if (registerByPlugin) {
                 logger.info(TAG, "Load router map by arouter-auto-register plugin.");
             } else {
     			//加載routerMap
                 Set<String> routerMap;
                 if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                     logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                     routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                     if (!routerMap.isEmpty()) {
                         context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                     }
                     PackageUtils.updateVersion(context);
                 } else {
                     logger.info(TAG, "Load router map from cache.");
                     routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
                 }
    
                 //開始加載
                 startInit = System.currentTimeMillis();
                 for (String className : routerMap) {
                     if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                         // 加載ARouter$$RootXXX的IRouteRoot子類
                         ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                     } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                         // 加載ARouter$$InterceptorsXXX的IInterceptorGroup子類
                         ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                     } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                         // 加載ARouter$$ProvidersXXX的IProviderGroup子類
                         ((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() + "]");
         }
     }
    複製代碼

    從com.alibaba.android.arouter.routes這個包下面去掃描並加載到routerMap,本地進行緩存,若是是debug模式或者是新版本都須要重新進行掃描。而後對router進行分類並加載到Warehouse中進行管理。組件化

  4. 而後跟進_ARouter.afterInit方法post

    static void afterInit() {
         interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
     }
    複製代碼

    afterInit方法是跟使用Arouter跳轉activity的代碼很是像,它返回了一個InterceptorService進行賦值。跟進去看一下,ARouter.getInstance()返回了一個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));
         }
     }
    複製代碼

    對path進行了判空,接着用另外一種navigation方法獲取了一個PathReplaceService,這個咱們後面再看。再接着就是調用了extractGroup方法從path中獲取到路由的group,而後再調用build(String path, String 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方法比較相似,最後就返回了一個postcart對象。afterInit方法中最後就是拿着返回的postcart對象去調用navigation方法,navigation方法最終會調用到_ARouter的navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)方法。

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
         try {
             LogisticsCenter.completion(postcard);
         } catch (NoRouteFoundException ex) {
             logger.warning(Consts.TAG, ex.getMessage());
             if (debuggable()) {
                 runInMainThread(new Runnable() {
                     @Override
                     public void run() {
                         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);
         }
         if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
             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;
     }
    複製代碼

    代碼比較多,咱們看主要的,首先是調用了LogisticsCenter.completion(postcard),這個方法是去加載路由信息,(前面LogisticsCenter只加載了相關group的一些東西,具體group中的路由尚未加載),若是已經加載就獲取信息完成postcart對象其餘字段的賦值。而後就是在攔截器中(若是是GreenChannel則直接調用)調用_navigation(context, postcard, requestCode, callback)方法。

    public synchronized static void completion(Postcard postcard) {
         if (null == postcard) {
             throw new NoRouteFoundException(TAG + "No postcard!");
         }
         RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
         if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
             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 {
                 try {
                     IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                     iGroupInstance.loadInto(Warehouse.routes);
                     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);   // Reload
             }
         } 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<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                 Map<String, Integer> paramsType = routeMeta.getParamsType();
                 if (MapUtils.isNotEmpty(paramsType)) {
                     for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                         setValue(postcard,
                                 params.getValue(),
                                 params.getKey(),
                                 resultMap.get(params.getKey()));
                     }
                     postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                 }
                 postcard.withString(ARouter.RAW_URI, rawUri.toString());
             }
             switch (routeMeta.getType()) {
                 case 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);
                     postcard.greenChannel();    // Provider should skip all of interceptors
                     break;
                 case FRAGMENT:
                     postcard.greenChannel();    // Fragment needn't interceptors
                 default:
                     break;
             }
         }
     }
    複製代碼

    if (null == groupMeta)這段代碼中就是將這個path對應的group中的路由所有加載進來,而後又一次調用了completion(postcard)方法,下面就是對postcard進行賦值,switch語句中能夠看到PROVIDER、FRAGMENT就是直接不經過攔截器的,並且PROVIDER的建立由框架控制,若是有實例就不重複建立了。讓後就是真正的路由方法_navigation(context, postcard, requestCode, callback)

    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:
                 final Intent intent = new Intent(currentContext, postcard.getDestination());
                 intent.putExtras(postcard.getExtras());
                 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);
                 }
                 String action = postcard.getAction();
                 if (!TextUtils.isEmpty(action)) {
                     intent.setAction(action);
                 }
                 runInMainThread(new Runnable() {
                     @Override
                     public void run() {
                         startActivity(requestCode, currentContext, intent, postcard, callback);
                     }
                 });
                 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;
     }
    複製代碼

    簡單明瞭,ACTIVITY就直接跳轉了返回null,PROVIDER就返回provider的實現類,BOARDCAST、CONTENT_PROVIDER、FRAGMENT也是經過反射建立對應的實例對象而後返回。

從整個Arouter的初始化流程,你們應該就明白了,Arouter其實就是將類的相關信息與咱們給定的路由造成一個映射,這些映射關係在Arouter初始化的時候被加載進來,而後再經過咱們定義好的路由來找到咱們的類。因此咱們在組件化中就能夠經過路由來跳轉其餘module的activity。

相關文章
相關標籤/搜索