ARouter 路由框架源碼解析

前言

隨著項目業務邏輯和功能點日益遞增, 邏輯的耦合程度也逐漸升高, 組件化技術可以很好的解決這個問題, 公司大佬最近也在搞組件化工程, 我想是時候分析一下組件化的實現方案了, Alibaba 的 ARouter 是非常出名的一個庫, 筆者抱著學習的態度去了解其實現原理, 以便于對組件化有更深刻的了解.

GitHub

https://github.com/alibaba/ARouter

一. ARouter 的模塊區分

arouter-annotation

  • @Router
    • 作用在 Activity 上會在對應的 Module 上生成 ARouterGroupgroup_name1
    • 作用在 IProvider 接口的實現類上會在對應的 Module 上生成 ARouterProvidermodule_name 類
/**
 * Mark a page can be route by router.
 *
 * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
 * @version 1.0
 * @since 16/8/15 下午9:29
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {

    /**
     * 跳轉地址
     */
    String path();

    /**
     * 跳轉地址所屬的組, 必須是通用的組名(一般不填/或者填寫當前 module 名)
     */
    String group() default "";

    /**
     * 用于生成 java 文檔名(可忽略) 
     */
    String name() default "";

    /**
     * Extra data, can be set by user.
     * Ps. U should use the integer num sign the switch, by bits. 10001010101010
     */
    int extras() default Integer.MIN_VALUE;

    /**
     * 路由的優先級, 用于攔截路由的分發
     */
    int priority() default -1;
}
  • @Interceptor
    • 作用在 IInterceptor 的實現類上會在對應的 Module 上生成 ARouterProvidermodule_name 類
/**
 * Mark a interceptor to interception the route.
 * BE ATTENTION : This annotation can be mark the implements of #{IInterceptor} ONLY!!!
 *
 * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
 * @version 1.0
 * @since 16/8/23 14:03
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
    /**
     * The priority of interceptor, ARouter will be excute them follow the priority.
     */
    int priority();

    /**
     * The name of interceptor, may be used to generate javadoc.
     */
    String name() default "Default";
}

arouter-compiler

編譯時注解的掃描與相關類的生成

arouter-api

提供路由的基本服務

  • ARouter
    • 門面
  • _ARouter
    • 實現
  • LogisticsCenter
    • 邏輯中心
  • Postcard
    • 導航的明信片
  • Warehouse
    • 數組倉庫

整體模塊之間的依賴關系

graph TB
arouter-api-->arouter-annotation
arouter-compiler-->arouter-annotation

接下來開始分析 ARouter 的工作流程

二. 路由的初始化

  • 調用方式在 Application 中調用 ARouter.init(this);
  • 接下來分析一下路由初始化的源碼
    /**
     * ARouter.init
     */
    public static void init(Application application) {
        if (!hasInit) {
            // 用于 ARouter 的 logger 打印, 之后會忽略這部分的源碼
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            // 1. 調用了 _ARouter 中的 init 
            hasInit = _ARouter.init(application);
            if (hasInit) {
                // 2. 調用 _ARouter 中的 afterInit 處理初始化之后的相關操作
                _ARouter.afterInit();
            }
        }
    }

可以看到 ARouter 其實就是個外殼, 具體的實現均由 _ARouter 提供,
可見路由的初始化分為兩步進行

  • 調用 _ARouter 的 init 執行初始化操作
  • 調用了 _ARouter 的 afterInit() 執行善后操作

先從 _ARouter.init 開始

1. _ARouter.init

    /**
     * _ARouter.init
     */
    protected static synchronized boolean init(Application application) {
        mContext = application;
        // 1. 調用了 LogisticsCenter 的 init 方法
        LogisticsCenter.init(mContext, executor);
        // 2. 更新標記位
        hasInit = true;
        // 3. 綁定主線程的 Handler
        mHandler = new Handler(Looper.getMainLooper());
        return true;
    }

主要做了三件事情

  • 調用了 LogisticsCenter 的 init 方法
  • 標記為已經初始化完畢
  • 創建主線程的 Handler

接下來分析一下 LogisticsCenter 初始化做了哪些事情

1.1 LogisticsCenter 邏輯處理中心的初始化

    /**
     * LogisticsCenter.init, load all metas in memory. Demand initialization
     */
    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        // 給線程池賦值
        executor = tpe;
        try {
            // 嘗試使用插件注冊 ARouter 
            loadRouterMap();
            
            if (registerByPlugin) {
                // ...
            } else {
                // 這里我們著重分析非插件注冊的初始化實現
                Set<String> routerMap;
                // 1. 判斷是否是調試(調試會生成類), 若是調試, 則從生成文件夾下找尋生成類
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                    // 1.1 每次調試, 會通過編譯時注解, 生成相關文件保存到本地 com.alibaba.android.arouter.routes 文件夾下
                    // 當程序運行時, 通過 getFileNameByPackageName 找到對應的全限定名
                    routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                    if (!routerMap.isEmpty()) {
                        // 1.2 更新生成文件的集合
                        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 {
                    // 2. 若不是調試, 則直接從本地的緩存中獲取, 非調試環境, 不會生成新的代碼
                    routerMap = new HashSet<>(
                         context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE)
                                .getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>())
                    );
                }
                // 3. 遍歷集合, 將緩存的集合加載進相應的集合中
                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        // 3.1 加載根元素(com.alibaba.android.arouter.routes.ARouter$$Root$$XXX)
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // 3.2 加載攔截器標簽元素(com.alibaba.android.arouter.routes.ARouter$$Interceptors$$XXX)
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // 3.3 加載提供器元素(com.alibaba.android.arouter.routes.ARouter$$Providers$$XXX)
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }
            }
            // ...忽略日志代碼
        } catch (Exception e) {
            // ...忽略異常代碼
        }
    }
    
    /**
     * LogisticsCenter.loadRouterMap 若啟用了插件注冊 ARouter , 將會在這個方法中自動生成代碼
     */
    private static void loadRouterMap() {
        registerByPlugin = false;
        //auto generate register code by gradle plugin: arouter-auto-register
        // looks like below:
        // registerRouteRoot(new ARouter..Root..modulejava());
        // registerRouteRoot(new ARouter..Root..modulekotlin());
    }
    
    /**
     * Warehouse : ARouter 的數據倉庫
     */
    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();
        }
   }

通過 LogisticsCenter.init 的分析, 可知該方法起到了非常重要的作用, 主要做了以下工作

  • 嘗試通過插件去注冊 ARouter, 這里忽略
  • 獲取 Set<String> routerMap 的數據
    • Debug: 若是開發環境, 則會讀取編譯時注解在 com.alibaba.android.arouter.routes 文件夾下的生成文件, 添加到 routerMap 中
      • 不為空, 則通過 SharedPreferences 緩存到本地
    • Release: 若線上環境, 則直接從緩存中讀取
  • 遍歷 routerMap 集合
    • 反射實例化 com.alibaba.android.arouter.routes.ARouterRootXXX 對象, 調用 loadInto 添加到 Warehouse.groupsIndex 中
    • 反射實例化 com.alibaba.android.arouter.routes.ARouterInterceptorsXXX 對象, 調用 loadInto 添加到 Warehouse.interceptorsIndex 中
    • 反射實例化 com.alibaba.android.arouter.routes.ARouterProvidersXXX 對象, 調用 loadInto 添加到 Warehouse.providersIndex 中

1.2 生成類展示

/**
 * CreateTime: @Router 注解作用在類上時, 被生成
 * Description: 
 * 將所有被 @Router(path="", group="m2") 注解標記并且 group = m2 的類, 添加到 Warehouse.routes 中
 * Warehouse.routes(key = @Router中的path, value = @Router標記類的信息生成的RouteMeta對象)
 */
public class ARouter$$Group$$m2 implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/module/2", RouteMeta.build(RouteType.ACTIVITY, TestModule2Activity.class, "/module/2", "m2", null, -1, -2147483648));
    }
}

/**
 * CreateTime: 一個 Module 中若有 ARouter$$Group$$XXX 存在時, 被生成
 * Description: 
 * 收集一個 Module 下所有的 ARouter$$Group$$XXX 添加到 Warehouse.groupsIndex 中
 * Warehouse.groupsIndex (key = Group名, value = ARouter$$Group$$XXX.class)
 */
public class ARouter$$Root$$modulejava implements IRouteRoot {
    @Override
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("m2", ARouter$$Group$$m2.class);
    }
}


/**
 * CreateTime: @Router 注解作用在實現了 IProvider 的類上時, 被生成
 * Description: 
 * 收集一個 Module 下所有的 IProvider 實現類信息添加到 Warehouse.providersIndex 中
 * Warehouse.providersIndex(key = 實現的接口類的全限定名, value = @Router標記類的信息生成的RouteMeta對象);
 */
public class ARouter$$Providers$$app implements IProviderGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> providers) {
        providers.put("com.alibaba.android.arouter.demo.testservice.HelloService",
                RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648)
        );
    }
}


/**
 * CreateTime: @Interceptor 注解作用在實現了 IInterceptor 的類上時, 被生成
 * Description: 
 * 收集一個 Module 下所有的 IInterceptorGroup 實現類信息添加到 Warehouse.interceptorsIndex 中
 * Warehouse.interceptorsIndex (key = @Interceptor中的優先級信息, value = 實現類的類型)
 */
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
    @Override
    public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
        interceptors.put(7, Test1Interceptor.class);
    }
}

1.3 生成類之間的依賴關系

  • IRouteRoot 與 IRootGroup 之間的依賴關系

    • 一個 module 會生成一個 ARouterRootmodule_name 類

    • 一個 module 會根據 group 的不同, 生成多個 ARouterRootgroup_name 類

    • ARouterRootmodule_name.loadInto() 方法被調用時, 會將保存的 ARouterRootgroup_name.class 信息注入 Warehouse.groupsIndex 中

    • 通過 Warehouse.groupsIndex 可以找到 ARouterRootgroup_name 類

      • 實例化后調用其 ARouterRootgroup_name.loadInto() , 會將 @Route 標記類的信息注入 Warehouse.routes 中, 這是非常重要的一步, 在后面會展開分析
    • IRouteRoot 與 IRootGroup 之間的依賴關系.png
  • IProvider 生成類

    • IProvider 生成類.png
  • IInterceptor 生成類

    • IInterceptor 生成類.png

2. _ARouter.afterInit

    static void afterInit() {
        // 初始化攔截服務
        interceptorService = (InterceptorService) ARouter.getInstance()
            .build("/arouter/service/interceptor")
            .navigation();
    }

2.1 從 ARouter.build 方法開始分析

    /**
     * ARouter.build 
     */
    public Postcard build(String path) {
        return _ARouter.getInstance().build(path);
    }

    /**
     * ARouter.build 
     */
    @Deprecated
    public Postcard build(String path, String group) {
        return _ARouter.getInstance().build(path, group);
    }

    /**
     * ARouter.build 
     */
    public Postcard build(Uri url) {
        return _ARouter.getInstance().build(url);
    }

可以看到三個重載方法都調用了 _ARouter 的方法, 這里與 WindowManager 中的方法使用 WindowManagerGlobal 橋接的手法很類似

    /**
     * _ARouter.build
     * 使用默認的 Group 組, 構建 Postcard 明信片
     */
    protected Postcard build(String path) {
        if (TextUtils.isEmpty(path)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            // 1. 獲取 PathReplaceService 服務
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            // 調用 extractGroup 方法從 path 獲取路由的 group, 調用了重載方法
            return build(path, extractGroup(path));
        }
    }

    /**
     * _ARouter.build
     * 通過 Uri 來構建 Postcard 明信片
     */
    protected Postcard build(Uri uri) {
        if (null == uri || TextUtils.isEmpty(uri.toString())) {
            throw new HandlerException(Consts.TAG + "Parameter invalid!");
        } else {
            // 1. 獲取 PathReplaceService 服務
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                uri = pService.forUri(uri);
            }
            // 2. 通過 path/group/uri 來構建明信片
            return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);
        }
    }

    /**
     * _ARouter.build
     * 通過路徑和指定的 group(用于合并路由) 構建明信片
     */
    protected Postcard build(String path, String group) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            // 1. 獲取 PathReplaceService 服務
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            // 2. 通過 path / group 來構建明信片
            return new Postcard(path, group);
        }
    }

通過 _ARouter.build 重載方法可知有一個非常重要的點 通過調用 ARouter.navigation(PathReplaceService.class) 獲取了一個 PathReplaceService 服務, 接下來看看這個服務獲取的流程

    /**
     * ARouter.navigation
     */
    public <T> T navigation(Class<? extends T> service) {
        return _ARouter.getInstance().navigation(service);
    }
    
    /**
     * _ARouter.navigation
     */
    protected <T> T navigation(Class<? extends T> service) {
        try {
            // 1 通過全限定類名嘗試獲取 service 的 Postcard
            Postcard postcard = LogisticsCenter.buildProvider(service.getName());
            if (null == postcard) {
                // 嘗試通過簡單類名獲取 Postcard
                postcard = LogisticsCenter.buildProvider(service.getSimpleName());
            }
            // 2. 沒找到則返回 null
            if (null == postcard) {
                return null;
            }
            // 3. 找到了則調用 LogisticsCenter.completion 對這個 postcard 進行數據填充
            LogisticsCenter.completion(postcard);
            // 4. 通過 postcard.getProvider() 返回數據
            return (T) postcard.getProvider();
        } catch (NoRouteFoundException ex) {
            // ...
        }
    }
    
    /**
     * LogisticsCenter.buildProvider
     */
    public static Postcard buildProvider(String serviceName) {
        // Warehouse.providersIndex 在 LogisticsCenter.init 中進行數據注入, 在上面已經分析過
        // 通過 serviceName 獲取對應類的信息封裝對象 RouteMeta
        RouteMeta meta = Warehouse.providersIndex.get(serviceName);
        if (null == meta) {
            return null;
        } else {
            // 構建明信片
            return new Postcard(meta.getPath(), meta.getGroup());
        }
    }

可以看到 _ARouter.navigation 中主要處理了以下事務

  • 調用 LogisticsCenter.buildProvider 獲取要獲取服務的 Postcar
    • 通過 Warehouse.providersIndex 找到對應的 RouteMeta 對象 meta
    • 使用 meta 來構建 Postcard 對象
  • 沒找到則返回 null
  • 找到相應的 Postcard 則調用 LogisticsCenter.completion 對這個 Postcard 進行數據填充
    • LogisticsCenter.completion 這個方法非常重要, 放在后面分析
  • 經過 LogisticsCenter.completion 對 Postcard 進行數據填充之后, Postcard 中的 provider 就有了數據, 調用 Postcard.getProvider() 將所需服務對象返回

PathReplaceService 不為空的話, 會調用其 forString(path)/forUri(uri) 進行自定義解析(一般為空, ARouter 默認沒有實現這個接口)

小結

至此 ARouter.build 完成之后獲取到了一個 Postcard 對象, 然后會調用 Postcard.navigation() 獲取所需對象實例, 接下來分析一下 Postcard.navigation() 的流程

2.2 分析 Postcard.navigation() 方法流程

    /**
     * Postcard.navigation
     */
    public Object navigation() {
        return navigation(null);
    }

    /**
     * Postcard.navigation
     */
    public Object navigation(Context context) {
        return navigation(context, null);
    }

    /**
     * Postcard.navigation
     */
    public Object navigation(Context context, NavigationCallback callback) {
        // 最終還是回到了 ARouter.navigation 方法中
        return ARouter.getInstance().navigation(context, this, -1, callback);
    }

可以看到 Postcard.navigation 的一系列重載方法, 最終都會調用到 ARouter.navigation 中, 這里才是重頭戲的開始

接下來分析 ARouter.navigation(context, this, -1, callback) 這個方法的處理了哪些事務

    /**
     * ARouter.navigation
     */
    public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
        return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
    }
    
    /**
     * _ARouter.navigation
     */
    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            // 1. 通過 postcard 明信片, 進行導航查找
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            // ...回調一些沒有找到目標的操作
            return null;
        }
        // 2. 回調通過 postcard 明信片定位到了目標
        if (null != callback) {
            callback.onFound(postcard);
        }
        // 3. 判斷 postcard 是否為綠色通道, 若不是則啟動攔截器服務進行攔截
        if (!postcard.isGreenChannel()) {   // 必須運行在異步線程, 否則可能 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 {
            // 4. 回調 _navigation 方法
            return _navigation(context, postcard, requestCode, callback);
        }
        return null;
    }

    /**
     * _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()) {
            // 4.1 處理 Activity 的導航
            case ACTIVITY:
                // 4.1.1 構建 Intent 意圖
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                // 4.1.2 獲取并設置 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);
                }
                // 4.1.3 設置 Intent 的 Action
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }
                // 4.1.4 在主線程中處理 Activity 的啟動
                if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            startActivity(requestCode, currentContext, intent, postcard, callback);
                        }
                    });
                } else {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
                break;
            // 4.2 處理 PROVIDER("com.alibaba.android.arouter.facade.template.IProvider") 的導航
            case PROVIDER:
                return postcard.getProvider();
            // 4.3 處理 BOARDCAST/CONTENT_PROVIDER/FRAGMENT 的導航
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                Class fragmentMeta = postcard.getDestination();
                try {
                    // 4.3.1 反射創建 Fragment 對象
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    // 4.3.2 給 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) {
                    // ...
                }
            // 4.4 處理 METHOD/SERVICE 的導航
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

通過上面的源碼分析可以非常清晰的看到 _ARouter.navigation 主要做了以下的事情

  • 調用 LogisticsCenter.completion 方法, 對 postcard 明信片數據填充
  • 回調通過 postcard 明信片定位到了目標
  • 判斷 postcard 是否為綠色通道, 若不是則啟動攔截器服務進行攔截
  • 回調 _navigation 方法真正的執行導航
    • 處理 Activity 的跳轉
    • 處理 ARouter IProvider 的服務獲取
    • 處理 BOARDCAST/CONTENT_PROVIDER/FRAGMENT 的導航
    • 處理 METHOD/SERVICE 的導航

小結

可以看到 ARouter.navigation(context, this, -1, callback) 中最終會調用 LogisticsCenter.completion 進行數據注入, 前面 2.1 中提到 ARouter.navigation(Class<? extends T> service) 同樣也調用了這個方法, 其重要程度可見一斑

接下來我們看一下, 最后的操作, LogisticsCenter.completion 中到底對 Postcard 做了哪些操作

2.4 LogisticsCenter.completion

    /**
     * LogisticsCenter.completion
     */
    public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }
        // 1. 從緩存池中 Warehouse.routes 獲取對應的 ARouter$$Group$$xxx.class 類的信息封裝對象 routeMeta
        // 初始化的時候, 一定為 null, 在 LogisticsCenter.init 中, 只對 Warehouse.groupsIndex 進行了數據填充
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            // 2. 從索引池 Warehouse.groupsIndex 中獲取 IRouteGroup 對應的實體類型(ARouter$$Group$$xxx.class)
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
            if (null == groupMeta) {
                // ...異常日志
            } else {
                try {
                    // 2.1 實例化 IRouteGroup(ARouter$$Group$$xxx.class)
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    // 2.2 調用其 loadInto 方法, 將該 Group 下所有類的信息 RouteMeta 填充到 Warehouse.routes 中
                    iGroupInstance.loadInto(Warehouse.routes);
                    // 2.3 說明當前 group 下的 RouteMeta 已經解析到 Warehouse.routes 中了, 從集合中移除
                    Warehouse.groupsIndex.remove(postcard.getGroup());
                } catch (Exception e) {
                    // ... 異常日志
                }
                // 2.4 重新調用 LogisticsCenter.completion, 下次將會走到 3 中
                completion(postcard);   // Reload
            }
        } else {
            // 3. 給 postcard 注入數據
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());
            // 3.1 獲取 URI
            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // Try to set params into bundle.
                // 3.2 分割 uri 參數
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                // 3.3 獲取參數類型并且注入 postcard 中
                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()));
                    }
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }
                // 3.5 mBundle 向 Bundle 中保存 URI鍵值對
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }
            // 4. 根據 routeMeta 的類型信息處理相應的操作
            switch (routeMeta.getType()) {
                case PROVIDER:  // 4.1 為 ARouter 的 IProvider
                    // 4.1.1 構建實例對象
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    // 根據 IProvider 的信息從 providers 中獲取緩存的實例對象
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) {
                        IProvider provider;
                        try {
                            // 反射創建實例對象
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            // 添加到緩存池
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            // ...
                        }
                    }
                    // 4.1.2 給 postcard 注入實例對象
                    postcard.setProvider(instance);
                    // 4.1.3 IProvider 類型的目標不允許設置為不可攔截(綠色通道)
                    postcard.greenChannel();
                    break;
                case FRAGMENT :// 4.2 Fragment 的類型
                    // 4.2.1 FRAGMENT 類型的目標不允許設置為不可攔截(綠色通道)
                    postcard.greenChannel();
                default:
                    break;
            }
        }
    }

可見這個方法真的做了很多重要的事情

  • 從緩存池中 Warehouse.routes 獲取對應的 ARouterGroupxxx.class 類的信息封裝對象 routeMeta

  • routeMeta 在緩存中不存在

    • 從索引池 Warehouse.groupsIndex 中獲取 IRouteGroup 對應的實體類型 IRouteGroup(ARouterGroupxxx.class)
    • 實例化 IRouteGroup(ARouterGroupxxx.class)
    • 調用其 loadInto 方法, 將該 Group 下所有類的信息 RouteMeta 填充到 Warehouse.routes 中
    • 說明當前 group 下的 RouteMeta 已經解析到 Warehouse.routes 中了, 從集合中移除
    • 重新回調 LogisticsCenter.completion 方法, 下次不會走此分支
  • routeMeta 在緩存中存在

    • 給 postcard 注入 routeMeta 中保存的數據
  • routeMeta 的類型信息處理相應的操作

    • PROVIDER:

      • 構建 IProvider 對象
      • 給 postcard 注入實例對象
      • 設置為綠色通道(不可攔截)
    • FRAGMENT:

      • 設置 為綠色通道, 其 實例 在上面分析過的 LogisticsCenter._navigation 中構建

之后便可以通過 postcard.getProvider(); 返回 ARouter.navigation 的實例對象了

三. 結語

通過本次對 ARouter 進行分析, 借此厘清 ARouter 初始化的工作原理, 雖然說是初始化, 但可以發現很多核心代碼都一并被分析了, 里面的緩存思想和代碼設計是極好的, 非常值得學習, 筆者有很多分析不到位的地方, 希望大家能夠批評指出

筆者參考了 ARouter 的實現方式, 手寫一個 SRouter 庫, 以加深對 Router 的理解, 其中攔截器思想使用的 OkHttp

https://github.com/SharryChoo/SRouter

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容