溫馨提示×

溫馨提示×

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

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

ARouter的原理的使用

發(fā)布時間:2020-06-10 15:18:37 來源:億速云 閱讀:457 作者:元一 欄目:移動開發(fā)
  • 組件化或者模塊化開發(fā)模式,已逐漸成為熱浪的形式,使用這些模式可以讓我們程序更容易的擴(kuò)展、更方便的維護(hù)
    更快捷的同步開發(fā)與更簡單的單獨(dú)調(diào)試,而ARouter的出現(xiàn)就是讓組件間、模塊間是實(shí)現(xiàn)完全的獨(dú)立。
  • ARouter是:阿里巴巴自研路由框架,主要解決組件間、模塊間的 界面跳轉(zhuǎn) 問題。由于項(xiàng)目中采用了組件化架構(gòu)進(jìn)行開發(fā),通過 ARouter 實(shí)現(xiàn)了頁面的跳轉(zhuǎn),之前看它的源碼時忘了寫筆記,因此今天來重新對它的源碼進(jìn)行一次分析。

初始化

ARouter 在使用前需要通過調(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,這個 _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;
        // 獲取存儲 ClassName 集合的 routerMap(debug 模式下每次都會拿最新的)
        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)建對象后通過 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)建對象后通過 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)建對象后通過 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 下存儲 ClassName 的集合 routerMap。
2.若為 debug 模式或之前沒有解析過 routerMap,則通過 ClassUtils.getFileNameByPackageName 方法對指定 package 下的所有 ClassName 進(jìn)行解析并存入 SP。
3.若并非 debug 模式,并且之前已經(jīng)解析過,則直接從 SP 中取出。(debug 每次都需要更新,因?yàn)轭悤S著代碼的修改而變動)
4.遍歷 routerMap 中的 ClassName。

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

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

  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 的遍歷處理進(jìn)行控制
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());
    // 遍歷 path,通過 DefaultPoolExecutor 并發(fā)對 path 進(jìn)行處理
    for (final String path : paths) {
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                // 加載 path 對應(yīng)的 dex 文件
                DexFile dexfile = null;
                try {
                    if (path.endsWith(EXTRACTED_SUFFIX)) {
                            // zip 結(jié)尾通過 DexFile.loadDex 進(jìn)行加載
                        dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                    } else {
                            // 否則通過 new DexFile 加載
                        dexfile = new DexFile(path);
                    }
                    // 遍歷 dex 中的 Entry
                    Enumeration<String> dexEntries = dexfile.entries();
                    while (dexEntries.hasMoreElements()) {
                            // 如果是對應(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;
  }

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

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

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

初始化 Warehouse

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

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

  class Warehouse {
    // 保存 RouteGroup 對應(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 對應(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 就是一個純粹用來存放信息的倉庫類,它的數(shù)據(jù)的實(shí)際上是通過上面的幾個自動生成的類在 loadInto 中對 Warehouse 主動填入數(shù)據(jù)實(shí)現(xiàn)的。

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

可以發(fā)現(xiàn),初始化過程主要完成了對自動生成的路由相關(guān)類 RouteRoot、InterceptorProviderGroup 的加載,對它們通過反射構(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));
    }
  }

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

最后轉(zhuǎn)調(diào)到了 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 實(shí)際上就是對字符串處理,取出 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 進(jìn)行預(yù)處理。之后通過 path 與 group 構(gòu)建了一個 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)用 withInt、withDouble 等方法,就可以進(jìn)行參數(shù)的設(shè)置。例如 withInt 方法:

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

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

最后我們通過 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);
  }

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

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

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

  protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
            // 通過 LogisticsCenter.completion 對 postcard 進(jìn)行補(bǔ)全
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // ...
    }
    if (null != callback) {
        callback.onFound(postcard);
    }
    // 如果設(shè)置了 greenChannel,會跳過所有攔截器的執(zhí)行
    if (!postcard.isGreenChannel()) {   
            // 沒有跳過攔截器,對 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.通過 LogisticsCenter.completion 對 postcard 進(jìn)行補(bǔ)全。
2.如果 postcard 沒有設(shè)置 greenChannel,則對 postcard 的攔截器進(jìn)行執(zhí)行,執(zhí)行完成后調(diào)用 _navigation 方法真正實(shí)現(xiàn)跳轉(zhuǎn)。
3.如果 postcard 設(shè)置了 greenChannel,則直接跳過所有攔截器,調(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!");
    }
    // 通過 Warehouse.routes.get 嘗試獲取 RouteMeta
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
            // 若 routeMeta 為 null,可能是并不存在,或是還沒有加載進(jìn)來
            // 嘗試獲取 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 {
                // ...
            // 如果找到了對應(yīng)的 RouteGroup,則將其加載進(jìn)來并重新調(diào)用 completion 進(jìn)行補(bǔ)全
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(postcard.getGroup());
            // ...
            completion(postcard);   // Reload
        }
    } else {
            // 如果找到了對應(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());
        }
        // 對于 provider 和 fragment,進(jìn)行特殊處理
        switch (routeMeta.getType()) {
            case PROVIDER:
                    // 如果是一個 provider,嘗試從 Warehouse 中查找它的類并構(gòu)造對象,然后將其設(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 都會跳過攔截器
                postcard.greenChannel();
                break;
            case FRAGMENT:
                     // provider 和 fragment 都會跳過攔截器
                postcard.greenChannel();
            default:
                break;
        }
    }
  }

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

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

它包括了我們補(bǔ)全所需要的如 Destination、Class、path 等信息,在生成代碼時自動根據(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:
            // 對 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 直接返回對應(yīng)的 provider
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
                // 對于 broadcast、contentprovider、fragment,構(gòu)造對象,設(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),它會根據(jù) postcardtype 來分別處理:

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

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

Service 的獲取

ARouter 除了可以通過 ARouter.getInstance().build().navigation()這樣的方式實(shí)現(xiàn)頁面跳轉(zhuǎn)之外,還可以通過 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;
    }
  }

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

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

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

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

  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í)際上非常簡單,就是通過 Warehouse 中已經(jīng)初始化的 providersIndex 根據(jù) serviceName 獲取對應(yīng)的 RouteMeta,之后根據(jù) RouteMeta的 path 和 group 返回對應(yīng)的 Postcard

攔截器機(jī)制

通過前面的分析,可以發(fā)現(xiàn) ARouter 中存在一套攔截器機(jī)制,在 completion 的過程中對攔截器進(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);
  }

攔截器中主要通過 process 方法完成執(zhí)行過程,可以在其中對 postcard 進(jìn)行處理。而攔截器的執(zhí)行我們知道,是通過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í)行通過一個 Executor 執(zhí)行,它首先構(gòu)造了一個值為 interceptors 個數(shù)的 CountDownLatch,之后通過 _execute 方法進(jìn)行執(zhí)行:

注解處理

那么 ARouter 是如何自動生成 RouteRoot、RouteMetaProviderGroup、Provider、Interceptor 的子類的呢?

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

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

總結(jié)

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

初始化

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

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

路由

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

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

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

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


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

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

AI