帶你一步一步的解析ARouter 源碼

ARouter 是阿里推出的一款頁面路由框架。由於項目中採用了組件化架構進行開發,通過 ARouter 實現了頁面的跳轉,之前看它的源碼時忘了寫筆記,因此今天來重新對它的源碼進行一次分析。

順手留下GitHub鏈接,需要獲取相關面試或者面試寶典核心筆記PDF等內容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。
可以點擊關於我聯繫我獲取

本篇源碼解析基於 ARouter 1.2.4

初始化

ARouter 在使用前需要通過調用 Arouter.init方法並傳入 Application 進行初始化:

  /**
 * Init, it must be call before used router.
 */
  public static void init(Application application) {
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        hasInit = _ARouter.init(application);
        if (hasInit) {
            _ARouter.afterInit();
        }
        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
     }
  }

這裏調用到了 _ARouter.init,這個 _ARouter類纔是 ARouter 的核心類:

  protected static synchronized boolean init(Application application) {
    mContext = application;
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;

      // It's not a good idea.
     // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
     //     application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
   
     }
    return true;
   }

這裏實際上調用到了 LogisticsCenter.init

 public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;
    try {
        long startInit = System.currentTimeMillis();
        Set<String> routerMap;
        // 獲取存儲 ClassName 集合的 routerMap(debug 模式下每次都會拿最新的)
        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
            logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
            // 根據指定的 packageName 獲取 package 下的所有 ClassName
            routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
            if (!routerMap.isEmpty()) {
                    // 存入 SP 緩存
                context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
            }
        } else {
            logger.info(TAG, "Load router map from cache.");
            // release 模式下,已經緩存了 ClassName 列表
            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();
        // 遍歷 ClassName
        for (String className : routerMap) {
            if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                // 發現是 Root,加載類構建對象後通過 loadInto 加載進 Warehouse.groupsIndex
                ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                // 發現是 Interceptor,加載類構建對象後通過 loadInto 加載進 Warehouse.interceptorsIndex
                ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                // 發現是 ProviderGroup,加載類構建對象後通過 loadInto 加載進 Warehouse.providersIndex
                ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
            }
        }
        // ...
    } catch (Exception e) {
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}
這裏主要有如下幾步:

1.獲取 com.alibaba.android.arouter.routes 下存儲 ClassName 的集合 routerMap
2.若爲 debug 模式或之前沒有解析過 routerMap,則通過 ClassUtils.getFileNameByPackageName 方法對指定 package 下的所有 ClassName 進行解析並存入 SP。
3.若並非 debug 模式,並且之前已經解析過,則直接從 SP 中取出。(debug 每次都需要更新,因爲類會隨着代碼的修改而變動)
4.遍歷 routerMap 中的 ClassName

  • 如果是 RouteRoot,則加載類構建對象後通過 loadInto 加載進 Warehouse.groupsIndex
  • 如果是InterceptorGroup,則加載類構建對象後通過loadInto 加載進 Warehouse.interceptorsIndex
  • 如果是 ProviderGroup,則加載類構建對象後通過loadInto 加載進Warehouse.providersIndex`。

解析 ClassName

我們先看看 ClassUtils.getFileNameByPackageName 是如何對指定 package 下的 ClassName 集合進行解析的:

  public static Set<String> getFileNameByPackageName(Context context, final String packageName) {
    final Set<String> classNames = new HashSet<>();
    // 通過 getSourcePaths 方法獲取 dex 文件 path 集合
    List<String> paths = getSourcePaths(context);
    // 通過 CountDownLatch 對 path 的遍歷處理進行控制
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());
    // 遍歷 path,通過 DefaultPoolExecutor 併發對 path 進行處理
    for (final String path : paths) {
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                // 加載 path 對應的 dex 文件
                DexFile dexfile = null;
                try {
                    if (path.endsWith(EXTRACTED_SUFFIX)) {
                            // zip 結尾通過 DexFile.loadDex 進行加載
                        dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                    } else {
                            // 否則通過 new DexFile 加載
                        dexfile = new DexFile(path);
                    }
                    // 遍歷 dex 中的 Entry
                    Enumeration<String> dexEntries = dexfile.entries();
                    while (dexEntries.hasMoreElements()) {
                            // 如果是對應的 package 下的類,則添加其 className
                        String className = dexEntries.nextElement();
                        if (className.startsWith(packageName)) {
                            classNames.add(className);
                        }
                    }
                } catch (Throwable ignore) {
                    Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                } finally {
                    if (null != dexfile) {
                        try {
                            dexfile.close();
                        } catch (Throwable ignore) {
                        }
                    }
                    parserCtl.countDown();
                }
            }
        });
    }
    // 所有 path 處理完成後,繼續向下走
    parserCtl.await();
    Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
    return classNames;
  }

這裏的步驟比較簡單,主要是如下的步驟:

1.通過 getSourcePaths 方法獲取 dex 文件的 path 集合。
2.創建了一個 CountDownLatch 控制 dex 文件的並行處理,以加快速度。
3.遍歷 path 列表,通過 DefaultPoolExecutor 對 path 並行處理。
4.加載 path 對應的 dex 文件,並對其中的 Entry 進行遍歷,若發現了對應 package 下的 ClassName,將其加入結果集合。
5.所有 dex 處理完成後,返回結果

關於 getSourcePaths 如何獲取到的 dex 集合這裏就不糾結了,因爲我們的關注點不在這裏。

初始化 Warehouse

Warehouse 實際上就是倉庫的意思,它存放了 ARouter 自動生成的類(RouteRootInterceptorGroupProviderGroup)的信息。

我們先看看 Warehouse 類究竟是怎樣的

  class Warehouse {
    // 保存 RouteGroup 對應的 class 以及 RouteMeta
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // 保存 Provider 以及 RouteMeta
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // 保存 Interceptor 對應的 class 以及 Inteceptor
    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();
    }
  }

可以發現 Warehouse 就是一個純粹用來存放信息的倉庫類,它的數據的實際上是通過上面的幾個自動生成的類在 loadInto 中對 Warehouse 主動填入數據實現的。

例如我們打開一個自動生成的 IRouteRoot 的實現類:

 public class ARouter$$Root$$homework implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("homework", ARouter$$Group$$homework.class);
  }
 }

可以看到,它在 groupsIndex 中對這個 RouteRoot 中的 IRouteGroup 進行了註冊,也就是向 groupIndex 中註冊了 Route Group 對應的 IRouteGroup 類。其他類也是一樣,通過自動生成的代碼將數據填入 Map 或 List 中。

可以發現,初始化過程主要完成了對自動生成的路由相關類 RouteRootInterceptorProviderGroup 的加載,對它們通過反射構造後將信息加載進了 Warehouse 類中。

路由跳轉

Postcard 的創建

下面我們看看路由的跳轉是如何實現的,我們先看到 ARouter.build 方法:

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

它轉調到了 _ARouter 的 build 方法:

  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));
    }
  }

它首先通過 ARouter.navigation 獲取到了 PathReplaceService,它需要用戶進行實現,若沒有實現會返回 null,若有實現則調用了它的 forString 方法傳入了用戶的 Route Path 進行路徑的預處理。

最後轉調到了 build(path, group),group 通過 extractGroup 得到:

 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;
    }
  }

extractGroup 實際上就是對字符串處理,取出 Route 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);
    }
  }

build(path, group) 方法同樣也會嘗試獲取到 PathReplaceService 並對 path 進行預處理。之後通過 path 與 group 構建了一個 Postcard 類:

  public Postcard(String path, String group) {
       this(path, group, null, null);
  }

  public Postcard(String path, String group, Uri uri, Bundle bundle) {
    setPath(path);
    setGroup(group);
    setUri(uri);
    this.mBundle = (null == bundle ? new Bundle() : bundle);
  }

這裏最終調用到了 PostCard(path, group, uri, bundle),這裏只是進行了一些參數的設置。

之後,如果我們調用 withIntwithDouble 等方法,就可以進行參數的設置。例如 withInt 方法:

  public Postcard withInt(@Nullable String key, int value) {
    mBundle.putInt(key, value);
    return this;
  }

它實際上就是在對 Bundle 中設置對應的 key、value。

最後我們通過 navigation 即可實現最後的跳轉:

  public Object navigation() {
    return navigation(null);
  }

  public Object navigation(Context context) {
    return navigation(context, null);
  }

 public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
  }

  public void navigation(Activity mContext, int requestCode) {
    navigation(mContext, requestCode, null);
  }

  public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
    ARouter.getInstance().navigation(mContext, this, requestCode, callback);
  }

通過如上的 navigation 可以看到,實際上它們都是最終調用到 ARouter.navigation 方法,在沒有傳入 Context 時會使用 Application 初始化的 Context,並且可以通過 NavigationCallbacknavigation 的過程進行監聽。

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

ARouter 仍然只是將請求轉發到了 _ARouter

  protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
            // 通過 LogisticsCenter.completion 對 postcard 進行補全
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // ...
    }
    if (null != callback) {
        callback.onFound(postcard);
    }
    // 如果設置了 greenChannel,會跳過所有攔截器的執行
    if (!postcard.isGreenChannel()) {   
            // 沒有跳過攔截器,對 postcard 的所有攔截器進行執行
        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);
                }
                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    } else {
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
  }

上面的代碼主要有以下步驟:

1.通過 LogisticsCenter.completion 對 postcard 進行補全。
2.如果 postcard 沒有設置 greenChannel,則對 postcard 的攔截器進行執行,執行完成後調用 _navigation 方法真正實現跳轉。
3.如果 postcard 設置了 greenChannel,則直接跳過所有攔截器,調用 _navigation 方法真正實現跳轉。

Postcard 的補全

我們看看 LogisticsCenter.completion 是如何實現 postcard 的補全的:

  public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }
    // 通過 Warehouse.routes.get 嘗試獲取 RouteMeta
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
            // 若 routeMeta 爲 null,可能是並不存在,或是還沒有加載進來
            // 嘗試獲取 postcard 的 RouteGroup
        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 {
                // ...
            // 如果找到了對應的 RouteGroup,則將其加載進來並重新調用 completion 進行補全
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(postcard.getGroup());
            // ...
            completion(postcard);   // Reload
        }
    } else {
            // 如果找到了對應的 routeMeta,將它的信息設置進 postcard 中
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());
        Uri rawUri = postcard.getUri();
                // 將 uri 中的參數設置進 bundle 中
        if (null != rawUri) {
            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());
        }
        // 對於 provider 和 fragment,進行特殊處理
        switch (routeMeta.getType()) {
            case PROVIDER:
                    // 如果是一個 provider,嘗試從 Warehouse 中查找它的類並構造對象,然後將其設置到 provider
                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);
                // provider 和 fragment 都會跳過攔截器
                postcard.greenChannel();
                break;
            case FRAGMENT:
                     // provider 和 fragment 都會跳過攔截器
                postcard.greenChannel();
            default:
                break;
        }
    }
  }

這個方法主要完成了對 postcard 的信息與 Warehouse 的信息進行結合,以補全 postcard 的信息,它的步驟如下:

1.通過 Warehouse.routes.get根據 path 嘗試獲取 RouteMeta 對象。
2.若獲取不到 RouteMeta 對象,可能是不存在或是還沒有進行加載(第一次都未加載),嘗試獲取 RouteGroup 調用其loadInto方法將 RouteMeta 加載進 Warehouse,最後調用 completion 重新嘗試補全 。
3.將 RouteMeta 的信息設置到 postcard 中,其中會將rawUri 的參數設置進 Bundle。
4.對於 ProviderFragment 特殊處理,其中 Provider 會從 Warehouse 中加載並構造它的對象,然後設置到 postcardProviderFragment 都會跳過攔截器。

RouteGrouploadInto 仍然是自動生成的,例如下面就是一些自動生成的代碼:

  public void loadInto(Map<String, RouteMeta> atlas) {
  atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, "/homework/commit", "homework", null, -1, -2147483648));
    // ...
  }

它包括了我們補全所需要的如 Destination、Class、path 等信息,在生成代碼時自動根據註解進行生成。

執行跳轉

我們看看 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()) {
        case ACTIVITY:
            // 對 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)) {    // Non activity, need less one flag.
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            // 切換到主線程,根據是否需要 result 調用不同的 startActivity 方法
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                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;
        case PROVIDER:
                // provider 直接返回對應的 provider
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
                // 對於 broadcast、contentprovider、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;
  }

可以發現,它會根據 postcardtype 來分別處理:

  • 對於 Activity,會構造一個 Intent 並將之前 postcard 中的參數設置進去,之後會根據是否需要 result 調用不同的 startActivity 方法。
  • 對於 Provider,直接返回其對應的 provider 對象。
  • 對於 BroadcastContentProviderFragment,反射構造對象後,將參數設置進去並返回。

可以發現 ARouter 的初始化和路由跳轉的整體邏輯還是不難的,實際上就是對 ActivityFragment 的調轉過程進行了包裝。

Service 的獲取

ARouter 除了可以通過 ARouter.getInstance().build().navigation()這樣的方式實現頁面跳轉之外,還可以通過 ARouter.getInstance().navigation(XXService.class)這樣的方式實現跨越組件的服務獲取,我們看看它是如何實現的:

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

仍然跳轉到了_ARouter 中去實現:

  protected <T> T navigation(Class<? extends T> service) {
    try {
        Postcard postcard = LogisticsCenter.buildProvider(service.getName());
        // Compatible 1.0.5 compiler sdk.
        // Earlier versions did not use the fully qualified name to get the service
        if (null == postcard) {
            // No service, or this service in old version.
            postcard = LogisticsCenter.buildProvider(service.getSimpleName());
        }
        if (null == postcard) {
            return null;
        }
        LogisticsCenter.completion(postcard);
        return (T) postcard.getProvider();
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        return null;
    }
  }

這裏首先通過 LogisticsCenter.buildProvider傳入service.class 的 name 構建出了一個 postcard。

而在 ARouter 老版本中,並不是通過這樣一個完整的 name 來獲取 Service 的,而是通過 simpleName,下面爲了兼容老版本,在獲取不到時會嘗試用老版本的方式重新構建一次。

之後會通過 LogisticsCenter.completion 對 postcard 進行補全,最後通過 postcard.Provider 獲取對應的 Provider。

除了 buildProvider 之外,其他方法我們已經在前面進行過分析,就不再贅述了:

  public static Postcard buildProvider(String serviceName) {
    RouteMeta meta = Warehouse.providersIndex.get(serviceName);
    if (null == meta) {
        return null;
    } else {
        return new Postcard(meta.getPath(), meta.getGroup());
    }
  }

這裏實際上非常簡單,就是通過 Warehouse 中已經初始化的 providersIndex 根據 serviceName 獲取對應的 RouteMeta,之後根據 RouteMeta的 path 和 group 返回對應的 Postcard

攔截器機制

通過前面的分析,可以發現 ARouter 中存在一套攔截器機制,在 completion 的過程中對攔截器進行了執行,讓我們看看它的攔截器機制的實現。

我們先看到 IInterceptor 接口:

  public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
  }

攔截器中主要通過 process 方法完成執行過程,可以在其中對 postcard 進行處理。而攔截器的執行我們知道,是通過InterceptorServiceImpl.doInterceptions 實現的:

  if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
    checkInterceptorsInitStatus();
    if (!interceptorHasInit) {
        callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
        return;
    }
    LogisticsCenter.executor.execute(new Runnable() {
        @Override
        public void run() {
            CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
            try {
                _excute(0, interceptorCounter, postcard);
                interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                    callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                    callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                } else {
                    callback.onContinue(postcard);
                }
            } catch (Exception e) {
                callback.onInterrupt(e);
            }
        }
  
               } else {
                callback.onContinue(postcard);
     }

這裏的執行通過一個 Executor 執行,它首先構造了一個值爲 interceptors 個數的 CountDownLatch,之後通過 _execute 方法進行執行:

註解處理

那麼 ARouter 是如何自動生成 RouteRootRouteMetaProviderGroupProviderInterceptor 的子類的呢?

實際上 ARouter 是通過 AnnotationProcessor 配合 AutoService 實現的,而對於類的生成主要是通過 JavaPoet 實現了對 Java 文件的編寫,關於 JavaPoet 的具體使用可以看到其 GitHub 主頁https://github.com/xiangjiana/Android-MS

由於註解處理部分的代碼大部分就是獲取註解的屬性,並結合 JavaPoet生成每個 Element 對應的 Java 代碼,這塊的代碼比較多且並不複雜,這裏就不帶大家去看這部分的源碼了,有興趣的讀者可以看看 arouter-complier 包下的具體實現。

總結

ARouter 的核心流程主要分爲三部分:
編譯期註解處理
通過 AnnotationProcessor 配合 JavaPoet 實現了編譯期根據註解對 RouteRootRouteMetaProviderGroupProviderInterceptor 等類的代碼進行生成,在這些類中完成了對 Warehouse 中裝載註解相關信息的工作。

初始化

通過ARouter.init,可以對ARouter 進行初始化,它主要分爲兩個步驟:

1.遍歷 Apkdex 文件,查找存放自動生成類的包下的類的 ClassName 集合。其中爲了加快查找速度,通過一個線程池進行了異步查找,並通過 CountDownLatch 來等待所有異步查找任務的結束。這個查找過程在非 debug 模式下是有緩存的,因爲 release 的 Apk 其自動生成的類的信息必然不會變化
2.根據 ClassName 的類型,分別構建 RouteRootInterceptorGroupProviderGroup 的對象並調用了其 loadInto 方法將這些 Group 的信息裝載進 Warehouse,這個過程並不會將具體的 RouteMeta 裝載。這些 Group 中主要包含了一些其對應的下一級的信息(如 RouteGroup 的 Class 對象等),之後就只需要取出下一級的信息並從中裝載,不再需要遍歷 dex 文件。

路由

路由的過程,主要分爲以下幾步:

1. 通過 ARouter 中的 build(path) 方法構建出一個 Postcard,或直接通過其 navigate(serviceClass) 方法構建一個 Postcard。
2. 通過對 Postcard 中提供的一系列方法對這次路由進行配置,包括攜帶的參數,是否跳過攔截器等等。
3.通過 navigation 方法完成路由的跳轉,它的步驟如下:

  • a.通過LogisticsCenter.completion 方法根據 Postcard 的信息結合 Warehouse 中加載的信息對 Postcard 的 Destination、Type 等信息進行補全,這個過程中會實現對 RouteMeta 信息的裝載,並且對於未跳過攔截器的類會逐個調用攔截器進行攔截器處理。
  • b.根據補全後 Postcard 的具體類型,調用對應的方法進行路由的過程(如對於 Activity 調用 startActivity,對於 Fragment 構建對象並調用 setArgument)。

4.將 navigation 的結果返回(Activity 返回的就是 null)

順手留下GitHub鏈接,需要獲取相關面試或者面試寶典核心筆記PDF等內容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。
可以點擊關於我聯繫我獲取

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