Android之旅 -- ARouter 源碼分析(一)

美景總是好的

Android studio工程的結(jié)構(gòu)

Android Studio 目錄結(jié)構(gòu)
  • app -- demo application
  • arouter-annotation -- 注解相關(guān)的聲明, 其他工程都要依賴這個
  • arouter-api -- ARouter 框架的 API
  • arouter-compiler -- 編譯期對注解分析的庫
  • module-java module-kotlin -- demo 的子模塊

初始化 ARouter

初始化的序列圖
// XXXXXActivity(Application)
ARouter.init(getApplication());

來看下 ARouter.init 的實現(xiàn), 都很簡單的直接調(diào)用了_ARouter.java

// ARouter.java
public static void init(Application application) {
    ...
    hasInit = _ARouter.init(application);
    ...
}

繼續(xù)看 _ARouter.java 實現(xiàn)

// _ARouter.java
protected static synchronized boolean init(Application application) {
    mContext = application;

    // 實際初始化的地方
    LogisticsCenter.init(mContext, executor);

    hasInit = true;
    return true;
}

主要實現(xiàn)都在 LogisticsCenter.init 方法 中, 繼續(xù)查看

// LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    // ROUTE_ROOT_PAKCAGE is com.alibaba.android.arouter.routes
    // ClassUtils.getFileNameByPackageName 就是根據(jù)報名查找對應(yīng)報名下的類, 就不貼代碼了..
    List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

    // 遍歷獲取到的 class
    for (String className : classFileNames) {

        // check start with com.alibaba.android.arouter.routes.ARouter$$Root
        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
            ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);

        // check start with com.alibaba.android.arouter.routes.ARouter$$Interceptors
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
            ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
        
        // check start with com.alibaba.android.arouter.routes.ARouter$$Providers
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
            ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
        }
    }
}

可以看到初始化就是查找com.alibaba.android.arouter.routes包下的類, 獲取實例并強制轉(zhuǎn)化成IRouteRoot, IInterceptorGroup, IProviderGroup, 然后調(diào)用 loadInto 方法.

通過 demo 的代碼查找能看到有com.alibaba.android.arouter.routes.ARouter$$Root$$app 這樣的類

// ARouter$$Root$$app.java
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        // 以分組做為 key, 緩存到 routes. 
        // routes 是指向 Warehouse.groupsIndex 的引用
        routes.put("service", ARouter$$Group$$service.class);
        routes.put("test", ARouter$$Group$$test.class);
    }
}

可以看到這是在編譯期通過分析注解生成的代碼. ARouter$$Group$$service.class 也是生成的.

// ARouter$$Group$$service.java
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$service implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
        ...
    }
}

到這里可以看到demo代碼里定義的HelloServiceImpl.java 等出現(xiàn)了. 其實 ARouter 就是通過分析注解在編譯期自動生成了一些關(guān)聯(lián)代碼. 另外的 Interceptors, Providers 邏輯上類似.

Interceptors 是注冊了聲明 Interceptor 注解, 并實現(xiàn) IInterceptor 接口的類
Providers 是注冊了聲明 Route 注解, 并實現(xiàn)了 IProvider 接口的類

到此初始化工作都做完了, 總結(jié)一下 ARouter 會在編譯期根據(jù)注解聲明分析自動生成一些注入代碼, 初始化工作主要是把這些注解的對象和對注解的配置緩存到 Warehouse 的靜態(tài)對象中.


從Activity跳轉(zhuǎn)調(diào)用開始分析源碼

navigation序列圖
// 跳轉(zhuǎn)activity的調(diào)用
ARouter.getInstance().build("/test/activity2").navigation();
// ARouter.java 
public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}
// _ARouter.java
// group 默認(rèn)是傳進來的 path 第一部分內(nèi)容. 例如 path = /test/activity1, group會默認(rèn)為 test
// 如果手動聲明的,一定要手動傳遞, 不然會找不到

protected Postcard build(String path, String group) {
    return new Postcard(path, group);
}

這里就是直接返回了一個 Postcard 對象, 并保存了path, group. Postcard 是繼承了 RouteMeta
navigation方法最后都要調(diào)用的 _ARouter.java 中, 中間過程省略.我們直接看核心代碼

// _ARouter.java
// postcard 就是前面用build 方法構(gòu)造的對象實例
// requestCode 是區(qū)分 startAcitivity 的方式.如果不為-1, 就用startActivityForResult的方式啟動
// NavigationCallback 是對各種狀態(tài)的回調(diào). 
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        // 驗證是否能找到對應(yīng)的 postcard.path
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // 如果沒找到postcard的配置, 調(diào)用onLost回調(diào)方法, 或者系統(tǒng)配置的"降級服務(wù)"(DegradeService)回調(diào)
        if (null != callback) {
            callback.onLost(postcard);
        } else {    
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

    ... 
 }

navigation調(diào)用了LogisticsCenter.completion方法做驗證, 我們看下 LogisticsCenter.java 這個方法如何驗證 postcard, 然后再繼續(xù)看navigation方法下面的邏輯

// LogisticsCenter.java
public synchronized static void completion(Postcard postcard) {
    // 通過postcard 的 path 查找對應(yīng)的 RouteMeta
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());

    if (null == routeMeta) {
        // 如果沒找到, 可能是還沒裝載過, 需要根據(jù) group 查找對應(yīng)的 groups
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); 
        if (null == groupMeta) {
            // 如果沒找到, 拋出 NoRouteFoundException 錯誤方法結(jié)束
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");

        } else {
            // 返回 IRouteGroup 對象, 并調(diào)用 loadInto方法.
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);

            // 刪除 group 緩存
            Warehouse.groupsIndex.remove(postcard.getGroup());

            // 重新調(diào)用 completion 方法,此時對應(yīng)的 group 已經(jīng)緩存完成
            completion(postcard);   // Reload
        }

    } else {
        // 可以查找到 routeMeta, copy routeMeta 的原始數(shù)據(jù)到 postcard 中.
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        switch (routeMeta.getType()) {
        case PROVIDER: 
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
            IProvider instance = Warehouse.providers.get(providerMeta);

            // 初始化 provider 對象, 并調(diào)用初始化方法, 緩存到Warehouse.providers中.
            if (null == instance) {
                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());
                }
            }
            
            // 設(shè)置一個provider 引用
            postcard.setProvider(instance);

            // provider 默認(rèn)設(shè)置跳過攔截器
            postcard.greenChannel(); 
            break;
        case FRAGMENT:
            // fragment 默認(rèn)設(shè)置跳過攔截器
            postcard.greenChannel(); 
        default:
            break;
        }
    }
}

completion方法主要是對 group 做一次懶式加載, 我們繼續(xù)查看 navigation 方法下面的內(nèi)容.

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    ...

    // 執(zhí)行到這里就是找到了 postcard. 執(zhí)行對應(yīng)回調(diào)
    if (null != callback) {
        callback.onFound(postcard);
    }

    // 如果是"綠色通道", 則直接執(zhí)行_navigation方法, 目前只有provider, fragment 默認(rèn)是走綠色通道
    if (!postcard.isGreenChannel()) { 
        // interceptorService 是 ARouter 配置的默認(rèn)的攔截服務(wù)
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {

            public void onContinue(Postcard postcard) {
                _navigation(context, postcard, requestCode, callback);
            }

            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        // 綠色通道
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
 }

interceptorService 是 ARouter 配置的默認(rèn)的攔截器com.alibaba.android.arouter.core.InterceptorServiceImpl.java

public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    // 用線程池異步執(zhí)行
    LogisticsCenter.executor.execute(new Runnable() {
        public void run() {
            // 初始化一個同步計數(shù)類, 用攔截器的 size
            CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
            try {
                // 執(zhí)行攔截操作, 看到這猜是遞歸調(diào)用.直到 index 滿足條件, 退出遞歸.
                _excute(0, interceptorCounter, postcard);
            
                // 線程等待計數(shù)完成, 等待300秒...
                interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);

                // 判斷退出等待后的一些狀態(tài)...
                if (interceptorCounter.getCount() > 0) { 
                    callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                } else if (null != postcard.getTag()) { 
                    callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                } else {

                    // 沒有問題, 繼續(xù)執(zhí)行
                    callback.onContinue(postcard);
                }
            } catch (Exception e) {

                // 中斷
                callback.onInterrupt(e);
            }
        }
    });
}

我們繼續(xù)看看_excute方法

private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
    // 遞歸退出條件
    if (index < Warehouse.interceptors.size()) {
        // 獲取要執(zhí)行的攔截器
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);

        // 執(zhí)行攔截
        iInterceptor.process(postcard, new InterceptorCallback() {

            public void onContinue(Postcard postcard) {
                // 計數(shù)器減1
                counter.countDown();
                
                // 繼續(xù)執(zhí)行下一個攔截
                _excute(index + 1, counter, postcard);  
            }

            public void onInterrupt(Throwable exception) {
                // 當(dāng)被攔截后退出遞歸
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); 
                counter.cancel();
            }
        });
    }
}

和我們猜測的一樣, 一個標(biāo)準(zhǔn)的遞歸調(diào)用, 當(dāng)所有攔截器執(zhí)行后(假設(shè)都不做攔截), 最后還是要回到_navigation方法

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = null == context ? mContext : context;

    // 我們就先只分析 activity 的邏輯
    switch (postcard.getType()) {
    case ACTIVITY:
        // 初始化 intent, 把參數(shù)也添加上
        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)) { 
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }

         // 在 UI 線程進行 start activity
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            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());
                }

                // 動畫設(shè)置
                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;
    }
    return null;
}

對 IProvider 進行 navigation

主要實現(xiàn)是在LogisticsCenter.completion方法中對IProvider進行了一些分支處理

        switch (routeMeta.getType()) {
        case PROVIDER: 
            // 返回實現(xiàn)IProvider接口的類
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();

            // 在緩存中查找
            IProvider instance = Warehouse.providers.get(providerMeta);

            // 初始化 provider 對象, 并調(diào)用初始化方法, 緩存到Warehouse.providers中.
            if (null == instance) {
                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());
                }
            }
            
            // 設(shè)置一個provider 引用
            postcard.setProvider(instance);

            // provider 默認(rèn)設(shè)置跳過攔截器
            postcard.greenChannel(); 
            break;
    // 可以看 _navigation 方法就是直接返回在 completion 方法中是設(shè)置的引用
    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 PROVIDER:
                return postcard.getProvider();
  
        }

        return null;
    }

好了.到這里我們已經(jīng)知道了 ARouter 大概的已經(jīng)工作流程了, 總結(jié)一下.
初始化的時候把注解標(biāo)示的內(nèi)容注入到緩存中, 然后啟動跳轉(zhuǎn)的時候根據(jù)緩存查找對應(yīng)的類的實現(xiàn). 看上去也是挺簡單的, 主要的黑科技其實也就是在編譯期生成代碼. 下一步我們繼續(xù)會看下編譯期到底做了哪些內(nèi)容.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容