中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

ARouter的原理的使用

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

初始化

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)


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

黑河市| 湘西| 满城县| 连江县| 惠水县| 钟山县| 遂溪县| 乌拉特前旗| 延川县| 个旧市| 浦东新区| 明星| 偃师市| 赫章县| 农安县| 安乡县| 昂仁县| 禄劝| 平陆县| 定襄县| 广东省| 绵竹市| 江阴市| 利辛县| 信丰县| 建瓯市| 东乌珠穆沁旗| 井研县| 济源市| 聊城市| 大余县| 沁水县| 西城区| 增城市| 汾阳市| 玉溪市| 大洼县| 开江县| 红桥区| 建平县| 遵义市|