ARouter 是阿里推出的一款页面路由框架。因为项目中采用了组件化架构进行开发,经过 ARouter 实现了页面的跳转,以前看它的源码时忘了写笔记,所以今天来从新对它的源码进行一次分析。android
(顺手留下GitHub连接,须要获取相关面试或者面试宝典核心笔记PDF等内容的能够本身去找)
https://github.com/xiangjiana/Android-MSgit
本篇源码解析基于 ARouter 1.2.4github
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
集合进行解析的:app
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
处理完成后,返回结果less
关于 getSourcePaths
如何获取到的 dex
集合这里就不纠结了,由于咱们的关注点不在这里。
Warehouse 实际上就是仓库的意思,它存放了 ARouter 自动生成的类(RouteRoot
、InterceptorGroup
、ProviderGroup
)的信息。
咱们先看看 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 中。
能够发现,初始化过程主要完成了对自动生成的路由相关类 RouteRoot
、Interceptor
、ProviderGroup
的加载,对它们经过反射构造后将信息加载进了 Warehouse 类中。
下面咱们看看路由的跳转是如何实现的,咱们先看到 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)
,这里只是进行了一些参数的设置。
以后,若是咱们调用 withInt
、withDouble
等方法,就能够进行参数的设置。例如 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
方法真正实现跳转。
咱们看看 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
中加载并构造它的对象,而后设置到postcard
。Provider
和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
来分别处理:
postcard
中的参数设置进去,以后会根据是否须要 result 调用不一样的 startActivity
方法。Provider
,直接返回其对应的 provider 对象。Broadcast
、ContentProvider
、Fragment
,反射构造对象后,将参数设置进去并返回。能够发现 ARouter
的初始化和路由跳转的总体逻辑仍是不难的,实际上就是对 Activity
、Fragment
的调转过程进行了包装。
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
是如何自动生成 RouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
的子类的呢?
实际上 ARouter 是经过 AnnotationProcessor 配合 AutoService 实现的,而对于类的生成主要是经过 JavaPoet 实现了对 Java 文件的编写,关于 JavaPoet 的具体使用能够看到其 GitHub 主页https://github.com/xiangjiana/Android-MS
因为注解处理部分的代码大部分就是获取注解的属性,并结合 JavaPoet
生成每一个 Element 对应的 Java 代码,这块的代码比较多且并不复杂,这里就不带你们去看这部分的源码了,有兴趣的读者能够看看 arouter-complier
包下的具体实现。
ARouter 的核心流程主要分为三部分:
编译期注解处理
经过 AnnotationProcessor
配合 JavaPoet
实现了编译期根据注解对 RouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
等类的代码进行生成,在这些类中完成了对 Warehouse 中装载注解相关信息的工做。
经过ARouter.init
,能够对ARouter
进行初始化,它主要分为两个步骤:
1.遍历
Apk
的dex
文件,查找存放自动生成类的包下的类的ClassName
集合。其中为了加快查找速度,经过一个线程池进行了异步查找,并经过CountDownLatch
来等待全部异步查找任务的结束。这个查找过程在非 debug 模式下是有缓存的,由于 release 的 Apk 其自动生成的类的信息必然不会变化
2.根据ClassName
的类型,分别构建RouteRoot
、InterceptorGroup
、ProviderGroup
的对象并调用了其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