溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時(shí)間:2020-08-05 07:29:07 來(lái)源:網(wǎng)絡(luò) 閱讀:217 作者:Android丶VG 欄目:移動(dòng)開發(fā)

ARouter 是阿里推出的一款頁(yè)面路由框架。由于項(xiàng)目中采用了組件化架構(gòu)進(jìn)行開發(fā),通過(guò) ARouter 實(shí)現(xiàn)了頁(yè)面的跳轉(zhuǎn),之前看它的源碼時(shí)忘了寫筆記,因此今天來(lái)重新對(duì)它的源碼進(jìn)行一次分析。

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

本篇源碼解析基于 ARouter 1.2.4

初始化

ARouter 在使用前需要通過(guò)調(diào)用 Arouter.init方法并傳入 Application 進(jìn)行初始化:

  /**
 * 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.");
     }
  }

這里調(diào)用到了 _ARouter.init,這個(gè) _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;
   }

這里實(shí)際上調(diào)用到了 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;
        // 獲取存儲(chǔ) ClassName 集合的 routerMap(debug 模式下每次都會(huì)拿最新的)
        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
            logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
            // 根據(jù)指定的 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 模式下,已經(jīng)緩存了 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)) {
                // 發(fā)現(xiàn)是 Root,加載類構(gòu)建對(duì)象后通過(guò) loadInto 加載進(jìn) Warehouse.groupsIndex
                ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                // 發(fā)現(xiàn)是 Interceptor,加載類構(gòu)建對(duì)象后通過(guò) loadInto 加載進(jìn) Warehouse.interceptorsIndex
                ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
            } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                // 發(fā)現(xiàn)是 ProviderGroup,加載類構(gòu)建對(duì)象后通過(guò) loadInto 加載進(jìn) 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 下存儲(chǔ) ClassName 的集合 routerMap
2.若為 debug 模式或之前沒(méi)有解析過(guò) routerMap,則通過(guò) ClassUtils.getFileNameByPackageName 方法對(duì)指定 package 下的所有 ClassName 進(jìn)行解析并存入 SP。
3.若并非 debug 模式,并且之前已經(jīng)解析過(guò),則直接從 SP 中取出。(debug 每次都需要更新,因?yàn)轭悤?huì)隨著代碼的修改而變動(dòng))
4.遍歷 routerMap 中的 ClassName。

  • 如果是 RouteRoot,則加載類構(gòu)建對(duì)象后通過(guò) loadInto 加載進(jìn) Warehouse.groupsIndex。
  • 如果是InterceptorGroup,則加載類構(gòu)建對(duì)象后通過(guò)loadInto 加載進(jìn) Warehouse.interceptorsIndex。
  • 如果是 ProviderGroup,則加載類構(gòu)建對(duì)象后通過(guò)loadInto 加載進(jìn)Warehouse.providersIndex`。
解析 ClassName

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

  public static Set<String> getFileNameByPackageName(Context context, final String packageName) {
    final Set<String> classNames = new HashSet<>();
    // 通過(guò) getSourcePaths 方法獲取 dex 文件 path 集合
    List<String> paths = getSourcePaths(context);
    // 通過(guò) CountDownLatch 對(duì) path 的遍歷處理進(jìn)行控制
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());
    // 遍歷 path,通過(guò) DefaultPoolExecutor 并發(fā)對(duì) path 進(jìn)行處理
    for (final String path : paths) {
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                // 加載 path 對(duì)應(yīng)的 dex 文件
                DexFile dexfile = null;
                try {
                    if (path.endsWith(EXTRACTED_SUFFIX)) {
                            // zip 結(jié)尾通過(guò) DexFile.loadDex 進(jìn)行加載
                        dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                    } else {
                            // 否則通過(guò) new DexFile 加載
                        dexfile = new DexFile(path);
                    }
                    // 遍歷 dex 中的 Entry
                    Enumeration<String> dexEntries = dexfile.entries();
                    while (dexEntries.hasMoreElements()) {
                            // 如果是對(duì)應(yīng)的 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 處理完成后,繼續(xù)向下走
    parserCtl.await();
    Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
    return classNames;
  }

這里的步驟比較簡(jiǎn)單,主要是如下的步驟:

1.通過(guò) getSourcePaths 方法獲取 dex 文件的 path 集合。
2.創(chuàng)建了一個(gè) CountDownLatch 控制 dex 文件的并行處理,以加快速度。
3.遍歷 path 列表,通過(guò) DefaultPoolExecutor 對(duì) path 并行處理。
4.加載 path 對(duì)應(yīng)的 dex 文件,并對(duì)其中的 Entry 進(jìn)行遍歷,若發(fā)現(xiàn)了對(duì)應(yīng) package 下的 ClassName,將其加入結(jié)果集合。
5.所有 dex 處理完成后,返回結(jié)果

關(guān)于 getSourcePaths 如何獲取到的 dex 集合這里就不糾結(jié)了,因?yàn)槲覀兊年P(guān)注點(diǎn)不在這里。

初始化 Warehouse

Warehouse 實(shí)際上就是倉(cāng)庫(kù)的意思,它存放了 ARouter 自動(dòng)生成的類(RouteRoot、InterceptorGroup、ProviderGroup)的信息。

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

  class Warehouse {
    // 保存 RouteGroup 對(duì)應(yīng)的 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 對(duì)應(yīng)的 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();
    }
  }

可以發(fā)現(xiàn) Warehouse 就是一個(gè)純粹用來(lái)存放信息的倉(cāng)庫(kù)類,它的數(shù)據(jù)的實(shí)際上是通過(guò)上面的幾個(gè)自動(dòng)生成的類在 loadInto 中對(duì) Warehouse 主動(dòng)填入數(shù)據(jù)實(shí)現(xiàn)的。

例如我們打開一個(gè)自動(dòng)生成的 IRouteRoot 的實(shí)現(xiàn)類:

 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 中對(duì)這個(gè) RouteRoot 中的 IRouteGroup 進(jìn)行了注冊(cè),也就是向 groupIndex 中注冊(cè)了 Route Group 對(duì)應(yīng)的 IRouteGroup 類。其他類也是一樣,通過(guò)自動(dòng)生成的代碼將數(shù)據(jù)填入 Map 或 List 中。

可以發(fā)現(xiàn),初始化過(guò)程主要完成了對(duì)自動(dòng)生成的路由相關(guān)類 RouteRoot、InterceptorProviderGroup 的加載,對(duì)它們通過(guò)反射構(gòu)造后將信息加載進(jìn)了 Warehouse 類中。

路由跳轉(zhuǎn)
Postcard 的創(chuàng)建

下面我們看看路由的跳轉(zhuǎn)是如何實(shí)現(xiàn)的,我們先看到 ARouter.build 方法:

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

它轉(zhuǎn)調(diào)到了 _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));
    }
  }

它首先通過(guò) ARouter.navigation 獲取到了 PathReplaceService,它需要用戶進(jìn)行實(shí)現(xiàn),若沒(méi)有實(shí)現(xiàn)會(huì)返回 null,若有實(shí)現(xiàn)則調(diào)用了它的 forString 方法傳入了用戶的 Route Path 進(jìn)行路徑的預(yù)處理。

最后轉(zhuǎn)調(diào)到了 build(path, group),group 通過(guò) 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 實(shí)際上就是對(duì)字符串處理,取出 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) 方法同樣也會(huì)嘗試獲取到 PathReplaceService 并對(duì) path 進(jìn)行預(yù)處理。之后通過(guò) path 與 group 構(gòu)建了一個(gè) 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);
  }

這里最終調(diào)用到了 PostCard(path, group, uri, bundle),這里只是進(jìn)行了一些參數(shù)的設(shè)置。

之后,如果我們調(diào)用 withIntwithDouble 等方法,就可以進(jìn)行參數(shù)的設(shè)置。例如 withInt 方法:

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

它實(shí)際上就是在對(duì) Bundle 中設(shè)置對(duì)應(yīng)的 key、value。

最后我們通過(guò) navigation 即可實(shí)現(xiàn)最后的跳轉(zhuǎn):

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

通過(guò)如上的 navigation 可以看到,實(shí)際上它們都是最終調(diào)用到 ARouter.navigation 方法,在沒(méi)有傳入 Context 時(shí)會(huì)使用 Application 初始化的 Context,并且可以通過(guò) NavigationCallback 對(duì) navigation 的過(guò)程進(jìn)行監(jiān)聽(tīng)。

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

ARouter 仍然只是將請(qǐng)求轉(zhuǎn)發(fā)到了 _ARouter

  protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
            // 通過(guò) LogisticsCenter.completion 對(duì) postcard 進(jìn)行補(bǔ)全
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // ...
    }
    if (null != callback) {
        callback.onFound(postcard);
    }
    // 如果設(shè)置了 greenChannel,會(huì)跳過(guò)所有攔截器的執(zhí)行
    if (!postcard.isGreenChannel()) {   
            // 沒(méi)有跳過(guò)攔截器,對(duì) postcard 的所有攔截器進(jìn)行執(zhí)行
        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.通過(guò) LogisticsCenter.completion 對(duì) postcard 進(jìn)行補(bǔ)全。
2.如果 postcard 沒(méi)有設(shè)置 greenChannel,則對(duì) postcard 的攔截器進(jìn)行執(zhí)行,執(zhí)行完成后調(diào)用 _navigation 方法真正實(shí)現(xiàn)跳轉(zhuǎn)。
3.如果 postcard 設(shè)置了 greenChannel,則直接跳過(guò)所有攔截器,調(diào)用 _navigation 方法真正實(shí)現(xiàn)跳轉(zhuǎn)。

Postcard 的補(bǔ)全

我們看看 LogisticsCenter.completion 是如何實(shí)現(xiàn) postcard 的補(bǔ)全的:

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

這個(gè)方法主要完成了對(duì) postcard 的信息與 Warehouse 的信息進(jìn)行結(jié)合,以補(bǔ)全 postcard 的信息,它的步驟如下:

1.通過(guò) Warehouse.routes.get根據(jù) path 嘗試獲取 RouteMeta 對(duì)象。
2.若獲取不到 RouteMeta 對(duì)象,可能是不存在或是還沒(méi)有進(jìn)行加載(第一次都未加載),嘗試獲取 RouteGroup 調(diào)用其loadInto方法將 RouteMeta 加載進(jìn) Warehouse,最后調(diào)用 completion 重新嘗試補(bǔ)全 。
3.將 RouteMeta 的信息設(shè)置到 postcard 中,其中會(huì)將rawUri 的參數(shù)設(shè)置進(jìn) Bundle。
4.對(duì)于 ProviderFragment 特殊處理,其中 Provider 會(huì)從 Warehouse 中加載并構(gòu)造它的對(duì)象,然后設(shè)置到 postcard。ProviderFragment 都會(huì)跳過(guò)攔截器。

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

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

它包括了我們補(bǔ)全所需要的如 Destination、Class、path 等信息,在生成代碼時(shí)自動(dòng)根據(jù)注解進(jìn)行生成。

執(zhí)行跳轉(zhuǎn)

我們看看 navigation 方法是如何實(shí)現(xiàn)的跳轉(zhuǎn):

  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:
            // 對(duì) Activity,構(gòu)造 Intent,將參數(shù)設(shè)置進(jìn)去
            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);
            }
            // 切換到主線程,根據(jù)是否需要 result 調(diào)用不同的 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 直接返回對(duì)應(yīng)的 provider
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
                // 對(duì)于 broadcast、contentprovider、fragment,構(gòu)造對(duì)象,設(shè)置參數(shù)后返回
            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;
  }

可以發(fā)現(xiàn),它會(huì)根據(jù) postcardtype 來(lái)分別處理:

  • 對(duì)于 Activity,會(huì)構(gòu)造一個(gè) Intent 并將之前 postcard 中的參數(shù)設(shè)置進(jìn)去,之后會(huì)根據(jù)是否需要 result 調(diào)用不同的 startActivity 方法。
  • 對(duì)于 Provider,直接返回其對(duì)應(yīng)的 provider 對(duì)象。
  • 對(duì)于 Broadcast、ContentProviderFragment,反射構(gòu)造對(duì)象后,將參數(shù)設(shè)置進(jìn)去并返回。

可以發(fā)現(xiàn) ARouter 的初始化和路由跳轉(zhuǎn)的整體邏輯還是不難的,實(shí)際上就是對(duì) ActivityFragment 的調(diào)轉(zhuǎn)過(guò)程進(jìn)行了包裝。

Service 的獲取

ARouter 除了可以通過(guò) ARouter.getInstance().build().navigation()這樣的方式實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)之外,還可以通過(guò) ARouter.getInstance().navigation(XXService.class)這樣的方式實(shí)現(xiàn)跨越組件的服務(wù)獲取,我們看看它是如何實(shí)現(xiàn)的:

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

仍然跳轉(zhuǎn)到了_ARouter 中去實(shí)現(xiàn):

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

這里首先通過(guò) LogisticsCenter.buildProvider傳入service.class 的 name 構(gòu)建出了一個(gè) postcard。

而在 ARouter 老版本中,并不是通過(guò)這樣一個(gè)完整的 name 來(lái)獲取 Service 的,而是通過(guò) simpleName,下面為了兼容老版本,在獲取不到時(shí)會(huì)嘗試用老版本的方式重新構(gòu)建一次。

之后會(huì)通過(guò) LogisticsCenter.completion 對(duì) postcard 進(jìn)行補(bǔ)全,最后通過(guò) postcard.Provider 獲取對(duì)應(yīng)的 Provider。

除了 buildProvider 之外,其他方法我們已經(jīng)在前面進(jìn)行過(guò)分析,就不再贅述了:

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

這里實(shí)際上非常簡(jiǎn)單,就是通過(guò) Warehouse 中已經(jīng)初始化的 providersIndex 根據(jù) serviceName 獲取對(duì)應(yīng)的 RouteMeta,之后根據(jù) RouteMeta的 path 和 group 返回對(duì)應(yīng)的 Postcard

攔截器機(jī)制

通過(guò)前面的分析,可以發(fā)現(xiàn) ARouter 中存在一套攔截器機(jī)制,在 completion 的過(guò)程中對(duì)攔截器進(jìn)行了執(zhí)行,讓我們看看它的攔截器機(jī)制的實(shí)現(xiàn)。

我們先看到 IInterceptor 接口:

  public interface IInterceptor extends IProvider {

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

攔截器中主要通過(guò) process 方法完成執(zhí)行過(guò)程,可以在其中對(duì) postcard 進(jìn)行處理。而攔截器的執(zhí)行我們知道,是通過(guò)InterceptorServiceImpl.doInterceptions 實(shí)現(xiàn)的:

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

這里的執(zhí)行通過(guò)一個(gè) Executor 執(zhí)行,它首先構(gòu)造了一個(gè)值為 interceptors 個(gè)數(shù)的 CountDownLatch,之后通過(guò) _execute 方法進(jìn)行執(zhí)行:

注解處理

那么 ARouter 是如何自動(dòng)生成 RouteRootRouteMeta、ProviderGroup、Provider、Interceptor 的子類的呢?

實(shí)際上 ARouter 是通過(guò) AnnotationProcessor 配合 AutoService 實(shí)現(xiàn)的,而對(duì)于類的生成主要是通過(guò) JavaPoet 實(shí)現(xiàn)了對(duì) Java 文件的編寫,關(guān)于 JavaPoet 的具體使用可以看到其 GitHub 主頁(yè)https://github.com/xiangjiana/Android-MS

由于注解處理部分的代碼大部分就是獲取注解的屬性,并結(jié)合 JavaPoet生成每個(gè) Element 對(duì)應(yīng)的 Java 代碼,這塊的代碼比較多且并不復(fù)雜,這里就不帶大家去看這部分的源碼了,有興趣的讀者可以看看 arouter-complier 包下的具體實(shí)現(xiàn)。

總結(jié)

ARouter 的核心流程主要分為三部分:
編譯期注解處理
通過(guò) AnnotationProcessor 配合 JavaPoet 實(shí)現(xiàn)了編譯期根據(jù)注解對(duì) RouteRootRouteMeta、ProviderGroup、Provider、Interceptor 等類的代碼進(jìn)行生成,在這些類中完成了對(duì) Warehouse 中裝載注解相關(guān)信息的工作。

初始化

通過(guò)ARouter.init,可以對(duì)ARouter 進(jìn)行初始化,它主要分為兩個(gè)步驟:

1.遍歷 Apkdex 文件,查找存放自動(dòng)生成類的包下的類的 ClassName 集合。其中為了加快查找速度,通過(guò)一個(gè)線程池進(jìn)行了異步查找,并通過(guò) CountDownLatch 來(lái)等待所有異步查找任務(wù)的結(jié)束。這個(gè)查找過(guò)程在非 debug 模式下是有緩存的,因?yàn)?release 的 Apk 其自動(dòng)生成的類的信息必然不會(huì)變化
2.根據(jù) ClassName 的類型,分別構(gòu)建 RouteRoot、InterceptorGroupProviderGroup 的對(duì)象并調(diào)用了其 loadInto 方法將這些 Group 的信息裝載進(jìn) Warehouse,這個(gè)過(guò)程并不會(huì)將具體的 RouteMeta 裝載。這些 Group 中主要包含了一些其對(duì)應(yīng)的下一級(jí)的信息(如 RouteGroup 的 Class 對(duì)象等),之后就只需要取出下一級(jí)的信息并從中裝載,不再需要遍歷 dex 文件。

路由

路由的過(guò)程,主要分為以下幾步:

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

  • a.通過(guò)LogisticsCenter.completion 方法根據(jù) Postcard 的信息結(jié)合 Warehouse 中加載的信息對(duì) Postcard 的 Destination、Type 等信息進(jìn)行補(bǔ)全,這個(gè)過(guò)程中會(huì)實(shí)現(xiàn)對(duì) RouteMeta 信息的裝載,并且對(duì)于未跳過(guò)攔截器的類會(huì)逐個(gè)調(diào)用攔截器進(jìn)行攔截器處理。
  • b.根據(jù)補(bǔ)全后 Postcard 的具體類型,調(diào)用對(duì)應(yīng)的方法進(jìn)行路由的過(guò)程(如對(duì)于 Activity 調(diào)用 startActivity,對(duì)于 Fragment 構(gòu)建對(duì)象并調(diào)用 setArgument)。

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

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

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI