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

美景總是好的

Android studio工程的結構

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

初始化 ARouter

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

來看下 ARouter.init 的實現, 都很簡單的直接調用了_ARouter.java

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

繼續看 _ARouter.java 實現

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

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

    hasInit = true;
    return true;
}

主要實現都在 LogisticsCenter.init 方法 中, 繼續查看

// LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    // ROUTE_ROOT_PAKCAGE is com.alibaba.android.arouter.routes
    // ClassUtils.getFileNameByPackageName 就是根據報名查找對應報名下的類, 就不貼代碼了..
    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包下的類, 獲取實例并強制轉化成IRouteRoot, IInterceptorGroup, IProviderGroup, 然后調用 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 等出現了. 其實 ARouter 就是通過分析注解在編譯期自動生成了一些關聯代碼. 另外的 Interceptors, Providers 邏輯上類似.

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

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


從Activity跳轉調用開始分析源碼

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

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

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

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

    ... 
 }

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

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

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

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

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

            // 重新調用 completion 方法,此時對應的 group 已經緩存完成
            completion(postcard);   // Reload
        }

    } else {
        // 可以查找到 routeMeta, copy routeMeta 的原始數據到 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 對象, 并調用初始化方法, 緩存到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());
                }
            }
            
            // 設置一個provider 引用
            postcard.setProvider(instance);

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

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

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

    // 執行到這里就是找到了 postcard. 執行對應回調
    if (null != callback) {
        callback.onFound(postcard);
    }

    // 如果是"綠色通道", 則直接執行_navigation方法, 目前只有provider, fragment 默認是走綠色通道
    if (!postcard.isGreenChannel()) { 
        // interceptorService 是 ARouter 配置的默認的攔截服務
        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 配置的默認的攔截器com.alibaba.android.arouter.core.InterceptorServiceImpl.java

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

                // 判斷退出等待后的一些狀態...
                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 {

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

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

我們繼續看看_excute方法

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

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

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

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

和我們猜測的一樣, 一個標準的遞歸調用, 當所有攔截器執行后(假設都不做攔截), 最后還是要回到_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, 把參數也添加上
        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());
                }

                // 動畫設置
                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

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

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

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

            // 初始化 provider 對象, 并調用初始化方法, 緩存到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());
                }
            }
            
            // 設置一個provider 引用
            postcard.setProvider(instance);

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容