Arouter學習筆記--分析源碼

頁面跳轉線分析

 ARouter.getInstance().build("/module1/mainActivity").navigation();

ARouter的方法都是通過_ARouter實現的

_ARouter.getInstance().build(path);

分析build方法 目的是構建Postcard對象 直接跟進build方法

  /**
     * Build postcard by path and default group
     */
    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));
        }
    }

extractGroup方法 分解path 這是爲什麼要嚴格遵守路徑路由的書寫規則了

  /**
     * Extract the default group from path.
     */
    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;
        }
    }

生成postcard對象 如例子:"/module1/mainActivity"
path="/module1/mainActivity",group=“module1”

   /**
     * Build postcard by path and 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);
        }
    }

分析navigation() 執行邏輯
postcard.navigation()–>ARouter.getInstance().navigation()–>_ARouter.getInstance().naigation();

核心方法 責任:處理註解邏輯 跳轉結果反饋 降級服務 攔截器邏輯

 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            // Pretreatment failed, navigation canceled.
            return null;
        }

        try {
          //物流中心  這是核心方法 責任是匹配註解相關的邏輯
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) {
                // Show friendly tips for user.
                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() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @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;
    }

順着主線 調用_navigation(context, postcard, requestCode, callback);
跳轉頁面類型ACTIVITY: postcard.getDestination() 對應目標頁面的class
目前版本 SERVICE、METHOD 還沒有實現 api 版本1.5.0

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.getDestination() 對應目標頁面的class
                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);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // Navigation in main looper.
                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;
    }

疑問?postcard.getDestination() 對應目標頁面的class
分析物流中心
1、從路由表倉庫中獲取路由 第一次一定獲取不到 RouteMeta routeMeta =Warehouse.routes.get(postcard.getPath());
2、獲取不到 去組裏獲取路由組 Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); 路由組的由來很重要
3、通過反射 構建組 然後加載路由表
4、從新執行一遍 completion 此時能獲取到路由對象 走正常分支

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 {
               // Load route and cache it into memory, then delete from metas.
               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();
                   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 {
           //設置目標class
           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 implement 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;
           }
       }
   }

兩個倉庫的生成邏輯
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();

在物流中心LogisticsCenter中 routes 通過路由組 IRouteGroup 加載的

ARouter$$Group$$module1 類是通過APT 結合註解信息生成的 
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$module1 implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module1/mainActivity", RouteMeta.build(RouteType.ACTIVITY, Module1_MainActivity.class, "/module1/mainactivity", "module1", new java.util.HashMap<String, Integer>(){{put("key", 8); }}, -1, -2147483648));
    atlas.put("/module1/server", RouteMeta.build(RouteType.PROVIDER, Module1Server.class, "/module1/server", "module1", null, -1, -2147483648));
  }
}

這裏通過組加載的好處是,不用一次性加載所有模塊的路由信息,在需要用到的時候在從組裏加載出來,節約內存和性能

groupsIndex 組信息加載 ,路由組是在框架初始化的時候加載的
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();

ARouter.init()–>_ARouter.init()–>LogisticsCenter.init()

在這裏插入圖片描述

1、從dex文件中找出 com.alibaba.android.arouter.routes包下的類的類名,緩存本地
2、通過類型反射 調用loadInto 方法 加載Warehouse.groupsIndex 路由組

 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;

                // It will rebuild router map every times when debuggable.
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                    logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                    // These class was generated by arouter-compiler.
                    //從dex文件中找出 com.alibaba.android.arouter.routes包下的類的類名
                    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);    // Save new version name when router map update finishes.
                } 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>()));
                }

                logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
                startInit = System.currentTimeMillis();
               //通過反射 調用loadInto 方法 加載Warehouse.groupsIndex 路由組
                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        // This one of root elements, load root.
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // Load providerIndex
                        ((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() + "]");
        }
    }

一次頁面跳轉源碼 完

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章