带你一步一步的解析ARouter 源码_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 带你一步一步的解析ARouter 源码

带你一步一步的解析ARouter 源码

 2020/1/6 18:24:32  Android丶SE  程序员俱乐部  我要评论(0)
  • 摘要:ARouter是阿里推出的一款页面路由框架。由于项目中采用了组件化架构进行开发,通过ARouter实现了页面的跳转,之前看它的源码时忘了写笔记,因此今天来重新对它的源码进行一次分析。(顺手留下GitHub链接,需要获取相关面试或者面试宝典核心笔记PDF等内容的可以自己去找)https://github.com/xiangjiana/Android-MS(更多完整项目下载。未完待续。源码。图文知识后续上传github。)(VX:mm14525201314)本篇源码解析基于ARouter1.2
  • 标签:源码 解析

ARouter 是阿里推出的一款页面路由框架。由于项目中采用了组件化架构进行开发,通过 ARouter 实现了页面的跳转,之前看它的源码时忘了写笔记,因此今天来重新对它的源码进行一次分析。

class="line" style="">(顺手留下GitHub链接,需要获取相关面试或者面试宝典核心笔记PDF等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

(更多完整项目下载。未完待续。源码。图文知识后续上传github。)
(VX:mm14525201314)

本篇源码解析基于 ARouter 1.2.4

初始化

ARouter 在使用前需要通过调用?monospace; padding: 2px 4px; background-color: #f6f6f6; vertical-align: middle; border: none; color: #c7254e; white-space: pre-wrap;">Arouter.init方法并传入?Application?进行初始化:

  /**
 * Init, it must be call before used router.
 */
  function" style="">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 类究竟是怎样的

javascript" style="font-size: 12px; font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; display: block; padding: 0px; background: transparent; color: #c7254e; vertical-align: middle; border: none;">  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,并且可以通过?NavigationCallback?对?navigation?的过程进行监听

  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.对于?Provider?和?Fragment?特殊处理,其中?Provider?会从?Warehouse?中加载并构造它的对象,然后设置到?postcardProvider?和?Fragment?都会跳过拦截器。

RouteGroup?的?loadInto?仍然是自动生成的,例如下面就是一些自动生成的代码:

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

可以发现,它会根据?postcard?的?type?来分别处理:

  • 对于 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.遍历?Apk?的?dex?文件,查找存放自动生成类的包下的类的?ClassName?集合。其中为了加快查找速度,通过一个线程池进行了异步查找,并通过?CountDownLatch?来等待所有异步查找任务的结束。这个查找过程在非 debug 模式下是有缓存的,因为 release 的 Apk 其自动生成的类的信息必然不会变化
2.根据?ClassName?的类型,分别构建?RouteRootInterceptorGroupProviderGroup?的对象并调用了其?loadInto?方法将这些 Group 的信息装载进 Warehouse,这个过程并不会将具体的?RouteMeta?装载。这些 Group 中主要包含了一些其对应的下一级的信息(如?RouteGroup?的 Class 对象等),之后就只需要取出下一级的信息并从中装载,不再需要遍历 dex 文件。

路由

路由的过程,主要分为以下几步:

1.?通过?ARouter?中的 build(path) 方法构建出一个 Postcard,或直接通过其?navigate(serviceClass)?方法构建一个 Postcard。
2.?通过对 Postcard 中提供的一系列方法对这次路由进行配置,包括携带的参数,是否跳过拦截器等等。
3.通过 navigation 方法完成路由的跳转,它的步骤如下:

  • a.通过LogisticsCenter.completion?方法根据 Postcard 的信息结合 Warehouse 中加载的信息对 Postcard 的 Destination、Type 等信息进行补全,这个过程中会实现对?RouteMeta?信息的装载,并且对于未跳过拦截器的类会逐个调用拦截器进行拦截器处理。
  • b.根据补全后 Postcard 的具体类型,调用对应的方法进行路由的过程(如对于 Activity 调用?startActivity,对于 Fragment 构建对象并调用?setArgument)。

4.将 navigation 的结果返回(Activity 返回的就是 null)

顺手留下GitHub链接,需要获取相关面试或者面试宝典核心笔记PDF等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

上一篇: php部署工具deployer技巧 - vendor目录创建 下一篇: 没有下一篇了!
发表评论
用户名: 匿名