ARouter 源碼學習

ARouter 源碼學習

官方文檔:
Android平臺頁面路由框架ARouterjava

阿里巴巴Arouter github地址以下:
ARouter gitHub 地址android

ARouter個人學習註釋GitHub地址:
ARoutergit

Arouter 組件化Demo:
Android_Modularization_Demogithub

強烈建議:閱讀ARouter源碼前,認真閱讀 ARouter官方文檔:Android平臺頁面路由框架ARouter,經過閱讀該文檔,會對Arouter的實現有一個大概瞭解,方便後邊的源碼閱讀。json

前一段時間,咱們的項目中引入了Arouter,引入的目的主要是爲了組件化功能解耦。所以以爲有必要認真學習一下Arouter的源碼,瞭解其工做原理。架構

如下爲官方文檔中給出的架構圖。app

這裏寫圖片描述

關乎Compiler部分的學習

ARouter 源碼學習之Compiler框架

API部分源碼學習

經過 ARouter 源碼學習之Compiler 咱們知道,Arouter工程通過build後,會在debug路徑下生成如下文件:ide

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

而這些文件的加載工做,是在ARouter.init(getApplication()); 初始化時完成的。
下面跟蹤源碼瞭解這一過程:組件化

Arouter初始化——加載編譯時生成的類

sequenceDiagram
participant ARouter as clientA
participant _ARouter as clientB
participant LogisticsCenter as serverA

Note over clientA:  Application中觸發初始化
clientA->>clientB: ARouter.init(getApplication())
clientB->>serverA: _ARouter.init(application)
serverA-->>serverA: LogisticsCenter.init\n(mContext, executor)\n加載編輯時生成的類
Note over clientA:  build下生成類加載完成

ARouter.init(getApplication());

/**
 * Init, it must be call before used router.
 * <p>
 * 一、通常在Application中完成初始化
 */
public static void init(Application application) {
    LogUtils.e("ARouter", "init");
    if (!hasInit) {
        // 日誌
        logger = _ARouter.logger;
        // 加載 生成的類
        hasInit = _ARouter.init(application);
        // 加載 攔截器
        if (hasInit) {
            _ARouter.afterInit();
        }
    }
}

其中_ARouter.init(application);用來記載build下生成的類文件。

_ARouter.init(application);

/**
 * 加載build下生成的類
 *
 * @param application
 * @return
 */
protected static synchronized boolean init(Application application) {
    mContext = application;
    // 加載生成的類
    LogisticsCenter.init(mContext, executor);
    // 初始化完成
    hasInit = true;
    return true;
}

這裏調用到了框架的物流中心LogisticsCenter中,用LogisticsCenter.init(mContext, executor); 方法完成了生成文件類的加載。

LogisticsCenter.init

/**
 * 基礎物流類初始化,加載生成的類
 * LogisticsCenter init, load all metas in memory. Demand initialization
 * <p>
 * <p>
 */
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    // 線程池
    executor = tpe;
    try {
        long startInit = System.currentTimeMillis();
        Set<String> routerMap;
        // 新版本 或者  debug包
        // It will rebuild router map every times when debuggable.
        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
            // These class was generate by arouter-compiler.
            // 獲取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 finish.
        } else {
            // 從sp中獲取數據
            routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
        }

        startInit = System.currentTimeMillis();
        // 循環com.alibaba.android.arouter.routes 路徑下的全部文件
        for (String className : routerMap) {
            // com.alibaba.android.arouter.routes.ARouter$$Root
            if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                // 反射
                // 建立 new ARouter$$Root$$app().loadInto();
                //
                // 將數據加載到Warehouse.groupsIndex中
                // routes.put("service", ARouter$$Group$$service.class);
                // routes.put("test", ARouter$$Group$$test.class);
                //
                // This one of root elements, load root.
                ((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)) {
                // 反射
                // 建立 new ARouter$$Interceptors$$app().loadInto(Warehouse.interceptorsIndex)
                // interceptors.put(7, Test1Interceptor.class);
                ((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)) {
                // new ARouter$$Providers$$app().loadInto();
                //
                // providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
                // providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
                // providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
                ((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(mContext, executor); 分別建立了 ARouter$$Root$$app.javaARouter$$Interceptors$$app.javaARouter$$Providers$$app.java 的對象,並分別調用了其 loadInto方法,對其內容進行了加載。

到這裏_ARouter.init(application);初始化完成,完成了bulid中如下類的加載

這裏寫圖片描述

按照官方文檔的說法,這裏是「分組管理,按需加載」。這裏加載了各個模塊的Root節點,而對於Root節點下的各個頁面,則暫不加載。

這裏寫圖片描述

官方文檔原話:
在運行期就須要將映射關係加載進來。而加載的時候就會遇到另外一個問題,由於須要面對長久的APP的設計,因此不可能一次性把全部的頁面都加載進來,當APP有一百或者幾百個頁面的時候,一次性將全部頁面都加載到內存中自己對於內存的損耗是很是可怕的,同時對於性能的損耗也是不可忽視的。因此ARouter中提出了分組的概念,ARouter容許某一個模塊下有多個分組,全部的分組最終會被一個root節點管理。如上圖中所示,假設有4個模塊,每一個模塊下面都有一個root結點,每一個root結點都會管理整個模塊中的group節點,每一個group結點則包含了該分組下的全部頁面

下邊回到ARouter.init(getApplication());方法中,跟蹤_ARouter.afterInit();方法的調用。

Arouter初始化——加載攔截器

build下生成類加載完成,下邊加載攔截器。

_ARouter.afterInit();

/**
 * afterInit
 * <p>
 * 用來加載攔截器
 * 完成類的加載後,由{@link ARouter.init}調用
 */
static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance()
            // 生成一個Postcard對象
            .build("/arouter/service/interceptor")
            // 返回一個Postcard
            //這個navigation()通過屢次調用以後,
            //最終調用的是_ARouter.navigation(context, postcard, requestCode, navigationCallback)方法
            .navigation();
}

這裏繼續跟蹤ARouter.getInstance().build("/arouter/service/interceptor")方法。

ARouter.build("/arouter/service/interceptor")

/**
 * Build the roadmap, draw a postcard.
 * <p>
 * 根據path 查找對應的 Postcard
 *
 * @param path Where you go.
 */
public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}

這裏又調用到了_ARouter 中的build(path)方法。

_ARouter.build("/arouter/service/interceptor")

/**
 * Build postcard by path and default group
 * <p>
 * 處理PathReplaceService 並更改URL地址
 *
 * @param path Postcard
 * @return 返回對應path地址的Postcard
 */
protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        // navigation(clazz)這種方式是屬於根據類型查找,而build(path)是根據名稱進行查找
        // 若是應用中沒有實現PathReplaceService這個接口,則pService=null
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        // 更改URL地址
        if (null != pService) {
            path = pService.forString(path);
        }
        // 返回對應URl地址的Postcard
        return build(path, extractGroup(path));
    }
}

這裏調用了ARouter.getInstance().navigation(PathReplaceService.class)返回了一個PathReplaceService對象,實際這裏加載到了PathReplaceServiceImpl

PathReplaceServiceImpl用於url地址替換需求:

這裏寫圖片描述

下邊繼續跟蹤代碼,查看是如何加載到PathReplaceServiceImpl的...

ARouter.navigation(PathReplaceService.class);

public <T> T navigation(Class<? extends T> service) {
    return _ARouter.getInstance().navigation(service);
}

繼續跟蹤_ARouter.getInstance().navigation(service)

_ARouter.navigation(PathReplaceService.class);

protected <T> T navigation(Class<? extends T> service) {
    LogUtils.e("_ARouter", "navigation: " + service.getSimpleName());
    Log.e("xiaxve_ARouter", "navigation: " + service.getName());
    try {
        // serviceName 爲 PathReplaceService 時,經過 Warehouse.providersIndex 找到 PathReplaceServiceImpl
        Postcard postcard = LogisticsCenter.buildProvider(service.getName());
        // Compatible 1.0.5 compiler sdk.
        if (null == postcard) { // No service, or this service in old version.
            postcard = LogisticsCenter.buildProvider(service.getSimpleName());
        }
        LogisticsCenter.completion(postcard);
        return (T) postcard.getProvider();
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        return null;
    }
}

終於找到PathReplaceServiceImpl

這裏 LogisticsCenter.buildProvider(service.getName())
經過 Warehouse.providersIndex
找到 RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/app/pathreplace", "app", null, -1, -2147483648)
最後返回一個new Postcard("/app/pathreplace", "app")
下邊繼續跟蹤LogisticsCenter.completion(postcard);

LogisticsCenter.completion(new Postcard("/app/pathreplace", "app"));

public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }
    // 第一次走到這裏 Warehouse.routes 尚未賦值
    // 所以返回的數據爲null
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        /**
         * 加載對應的組內數據
         */
        // 經過"app"找到 routes.put("app", ARouter$$Group$$app.class);
        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()));
                }
                // new ARouter$$Group$$app();
                IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                // 加載"app"組內內容
                // 這裏加載的是
                // atlas.put("/app/pathreplace",
                //            RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/app/pathreplace", "app", null, -1, -2147483648));
                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() + "]");
            }
            // 遞歸調用
            completion(postcard);   // Reload
        }
    }
    // 第二次加載時,已經找到
    // RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/app/pathreplace", "app", null, -1, -2147483648))
    else {
        // 給postcard賦值
        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.
            // ... 此次未調用到,暫時省略
        }

        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();
                // 第一次被調用時,Warehouse.providers尚未賦值,所以instance==null
                // 參數 PathReplaceServiceImpl.class
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        // new PathReplaceServiceImpl();
                        provider = providerMeta.getConstructor().newInstance();
                        // new PathReplaceServiceImpl().init(mContext);
                        provider.init(mContext);
                        // 添加到Warehouse.providers中
                        Warehouse.providers.put(providerMeta, provider);
                        // 
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                // 將new PathReplaceServiceImpl()保存在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;
        }
    }
}

關鍵代碼
該方法又體現了分組加載的思想,經過加載「app」節點下的"/app/pathreplace",完善了new Postcard("/app/pathreplace", "app")對象,經過反射建立了PathReplaceServiceImpl對象,並添加到了new Postcard("/app/pathreplace", "app")

  • 一、經過「app」分組找到 routes.put("app", ARouter$$Group$$app.class);
  • 二、經過反射建立new ARouter$$Group$$app()對象,並加載ARouter$$Group$$app()分組內的所有數據,其中就包含RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/app/pathreplace", "app", null, -1, -2147483648))
  • 三、遞歸調用,建立new PathReplaceServiceImpl();並添加到Warehouse.providersnew Postcard("/app/pathreplace", "app")

到這裏PathReplaceService的加載完成了,下邊咱們回到_ARouter.build("/arouter/service/interceptor")繼續攔截器的加載。

_ARouter.build("/arouter/service/interceptor")

這裏咱們回到_ARouter.build("/arouter/service/interceptor")方法,

protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        // navigation(clazz)這種方式是屬於根據類型查找,而build(path)是根據名稱進行查找
        // 若是應用中沒有實現PathReplaceService這個接口,則pService=null
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        // 這裏經過PathReplaceService,進行URL地址的更換
        if (null != pService) {
            path = pService.forString(path);
        }
        // 返回對應URl地址的Postcard
        return build(path, extractGroup(path));
    }
}

這裏最終返回new Postcard("/arouter/service/interceptor", "group")

下邊回到_ARouter.afterInit()方法

_ARouter.afterInit() 方法結束

/**
 * afterInit
 * <p>
 * 用來加載攔截器
 * <p>
 * 完成類的加載後,由{@link ARouter.init}調用
 */
static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance()
            // 生成一個Postcard對象
            .build("/arouter/service/interceptor")
            // 返回一個Postcard
            //這個navigation()通過屢次調用以後,
            //最終調用的是_ARouter.navigation(context, postcard, requestCode, navigationCallback)方法
            .navigation();
}

到這裏,下邊咱們要跟蹤的方法變爲new Postcard("/arouter/service/interceptor", "group").navigation();

而該調用,最終會調用到_ARouter

public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
    return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}

最終會返回InterceptorServiceImpl攔截器對象,並賦值給InterceptorService interceptorService

可能由於InterceptorServiceImpl類的存在,因此在官方文檔中,纔會說Arouter是自舉的吧

後邊的不想說了,若是看到了這裏,後邊的東西,也不用我再說了,就到這吧。

相關文章
相關標籤/搜索