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

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

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供黃梅網(wǎng)站建設(shè)、黃梅做網(wǎng)站、黃梅網(wǎng)站設(shè)計、黃梅網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、黃梅企業(yè)網(wǎng)站模板建站服務(wù),十年黃梅做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

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

本篇源碼解析基于 ARouter 1.2.4

初始化

ARouter 在使用前需要通過調(diào)用 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.");
     }
  }

這里調(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;
   }

這里實際上調(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 加載進 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 加載進 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 加載進 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 模式,并且之前已經(jīng)解析過,則直接從 SP 中取出。(debug 每次都需要更新,因為類會隨著代碼的修改而變動)
4.遍歷 routerMap 中的 ClassName。

  • 如果是 RouteRoot,則加載類構(gòu)建對象后通過 loadInto 加載進 Warehouse.groupsIndex。
  • 如果是InterceptorGroup,則加載類構(gòu)建對象后通過loadInto 加載進 Warehouse.interceptorsIndex。
  • 如果是 ProviderGroup,則加載類構(gòu)建對象后通過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 并發(fā)對 path 進行處理
    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 進行加載
                        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 進行遍歷,若發(fā)現(xiàn)了對應(yīng) package 下的 ClassName,將其加入結(jié)果集合。
5.所有 dex 處理完成后,返回結(jié)果

關(guān)于 getSourcePaths 如何獲取到的 dex 集合這里就不糾結(jié)了,因為我們的關(guān)注點不在這里。

初始化 Warehouse

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

我們先看看 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ù)的實際上是通過上面的幾個自動生成的類在 loadInto 中對 Warehouse 主動填入數(shù)據(jù)實現(xiàn)的。

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

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

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

下面我們看看路由的跳轉(zhuǎn)是如何實現(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,它需要用戶進行實現(xiàn),若沒有實現(xiàn)會返回 null,若有實現(xiàn)則調(diào)用了它的 forString 方法傳入了用戶的 Route Path 進行路徑的預(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 實際上就是對字符串處理,取出 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 進行預(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),這里只是進行了一些參數(shù)的設(shè)置。

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

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

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

最后我們通過 navigation 即可實現(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 可以看到,實際上它們都是最終調(diào)用到 ARouter.navigation 方法,在沒有傳入 Context 時會使用 Application 初始化的 Context,并且可以通過 NavigationCallbacknavigation 的過程進行監(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 進行補全
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // ...
    }
    if (null != callback) {
        callback.onFound(postcard);
    }
    // 如果設(shè)置了 greenChannel,會跳過所有攔截器的執(zhí)行
    if (!postcard.isGreenChannel()) {   
            // 沒有跳過攔截器,對 postcard 的所有攔截器進行執(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 進行補全。
2.如果 postcard 沒有設(shè)置 greenChannel,則對 postcard 的攔截器進行執(zhí)行,執(zhí)行完成后調(diào)用 _navigation 方法真正實現(xiàn)跳轉(zhuǎn)。
3.如果 postcard 設(shè)置了 greenChannel,則直接跳過所有攔截器,調(diào)用 _navigation 方法真正實現(xiàn)跳轉(zhuǎn)。

Postcard 的補全

我們看看 LogisticsCenter.completion 是如何實現(xiàn) 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 {
                // ...
            // 如果找到了對應(yīng)的 RouteGroup,則將其加載進來并重新調(diào)用 completion 進行補全
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(postcard.getGroup());
            // ...
            completion(postcard);   // Reload
        }
    } else {
            // 如果找到了對應(yīng)的 routeMeta,將它的信息設(shè)置進 postcard 中
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());
        Uri rawUri = postcard.getUri();
                // 將 uri 中的參數(shù)設(shè)置進 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 中查找它的類并構(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 的信息進行結(jié)合,以補全 postcard 的信息,它的步驟如下:

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

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 等信息,在生成代碼時自動根據(jù)注解進行生成。

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

我們看看 navigation 方法是如何實現(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è)置進去
            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ù)是否需要 result 調(diào)用不同的 startActivity 方法。
  • 對于 Provider,直接返回其對應(yīng)的 provider 對象。
  • 對于 Broadcast、ContentProviderFragment,反射構(gòu)造對象后,將參數(shù)設(shè)置進去并返回。

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

Service 的獲取

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

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

仍然跳轉(zhuǎn)到了_ARouter 中去實現(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 進行補全,最后通過 postcard.Provider 獲取對應(yīng)的 Provider。

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

  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 中已經(jīng)初始化的 providersIndex 根據(jù) serviceName 獲取對應(yīng)的 RouteMeta,之后根據(jù) RouteMeta的 path 和 group 返回對應(yīng)的 Postcard

攔截器機制

通過前面的分析,可以發(fā)現(xiàn) ARouter 中存在一套攔截器機制,在 completion 的過程中對攔截器進行了執(zhí)行,讓我們看看它的攔截器機制的實現(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 進行處理。而攔截器的執(zhí)行我們知道,是通過InterceptorServiceImpl.doInterceptions 實現(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 方法進行執(zhí)行:

注解處理

那么 ARouter 是如何自動生成 RouteRootRouteMeta、ProviderGroupProvider、Interceptor 的子類的呢?

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

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

總結(jié)

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

初始化

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

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

路由

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

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

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

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

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

網(wǎng)站標題:帶你一步一步的解析ARouter源碼
本文鏈接:http://muchs.cn/article34/pdpope.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、網(wǎng)站設(shè)計定制網(wǎng)站、虛擬主機、品牌網(wǎng)站建設(shè)、網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司