背景:

随着项目越做越大,代码量越来越多,项目也随之改造成组件化的开发模式,组件化开发非常适合庞大的项目,将每个业务模块,功能模块解耦,抽离成组件的形式,各个组件遵循严格的依赖关系。因为这层严格的依赖关系,使得组件化比模块化结构更加简洁和清晰,以前的模块化开发,模块间常常相互依赖,形成闭环,改动模块的代码会牵一发而动全身,稳定性和可维护性就显得特别弱小。

组件化严格控制组件间的依赖关系,组件间相互依赖是不允许存在的,所以我们需要一个框架来解决组件间的通信的问题。

ARouter的几个常用功能:

1.组件间的页面跳转。

2.依赖注入

3.拦截器。

关于ARouter如何实现上面三个功能的?

ARouter源码主要由三个模块组成:

ARouter-compiler: 这个模块主要用于解析注解,然后生成路由业务代码的java文件,主要需要阅读的源码文件是

RouterProcessor

InterceptorProcessor

AutowiredProcessor

ARouter-api:这个模块主要暴露一些接口和方法给业务层调用,还封装了一个线程池和一些异常等等,建议需要阅读的源码文件

thread包下的文件,理解为什么需要用到线程池

launcher包下的文件,理解初始化过程中做了哪些工作

Warehouse

LogisticsCenter

InterceptorServiceImpl

ARouter-annotation:定义了一些注解。

先看下页面跳转是如何实现的:

// 页面跳转
ARouter.getInstance().build("/test/activity").navigation();
/*** Build postcard by path and group*/protected Postcard build(String path, String group, Boolean afterReplace) {if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {if (!afterReplace) {PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}}return new Postcard(path, group);}}
/*** Use router navigation.** @param context     Activity or null.* @param postcard    Route metas* @param requestCode RequestCode* @param callback    cb*/protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {// Pretreatment failed, navigation canceled.return null;}// Set context to postcard.postcard.setContext(null == context ? mContext : context);try {LogisticsCenter.completion(postcard);} catch (NoRouteFoundException ex) {logger.warning(Consts.TAG, ex.getMessage());if (debuggable()) {// Show friendly tips for user.runInMainThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mContext, "There's no route matched!\n" +" Path = [" + postcard.getPath() + "]\n" +" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();}});}if (null != callback) {callback.onLost(postcard);} else {// No callback for this invoke, then we use the global degrade service.DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);if (null != degradeService) {degradeService.onLost(context, postcard);}}return null;}if (null != callback) {callback.onFound(postcard);}if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}/*** Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(postcard, requestCode, callback);}return null;}

build方法传入path和group(可不传),最后new一个postcard对象,Postcard对象 可以理解为一个封装了页面跳转所需要的内容,它包含页面跳转的参数,动画,context等信息。

在navigation方法里面实现页面跳转的代码是

 LogisticsCenter.completion(postcard);

然后继续跟

/*** Completion the postcard by route metas** @param postcard Incomplete postcard, should complete by this method.*/public synchronized static void completion(Postcard postcard) {if (null == postcard) {throw new NoRouteFoundException(TAG + "No postcard!");}RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());if (null == routeMeta) {// Maybe its does't exist, or didn't load.if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");} else {// Load route and cache it into memory, then delete from metas.try {if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));}addRouteGroupDynamic(postcard.getGroup(), null);if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));}} catch (Exception e) {throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");}completion(postcard);   // Reload}} else {postcard.setDestination(routeMeta.getDestination());postcard.setType(routeMeta.getType());postcard.setPriority(routeMeta.getPriority());postcard.setExtra(routeMeta.getExtra());Uri rawUri = postcard.getUri();if (null != rawUri) {   // Try to set params into bundle.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 @Paramfor (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 uripostcard.withString(ARouter.RAW_URI, rawUri.toString());}switch (routeMeta.getType()) {case PROVIDER:  // if the route is provider, should find its instance// Its provider, so it must implement IProviderClass<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();IProvider instance = Warehouse.providers.get(providerMeta);if (null == instance) { // There's no instance of this providerIProvider provider;try {provider = providerMeta.getConstructor().newInstance();provider.init(mContext);Warehouse.providers.put(providerMeta, provider);instance = provider;} catch (Exception e) {logger.error(TAG, "Init provider failed!", e);throw new HandlerException("Init provider failed!");}}postcard.setProvider(instance);postcard.greenChannel();    // Provider should skip all of interceptorsbreak;case FRAGMENT:postcard.greenChannel();    // Fragment needn't interceptorsdefault:break;}}}

我们看到代码会从Warehouse类里面获取RouteMeta对象,如果为null,表示根据路由名找不到路由的目标页面,这个时候,做了一次对group的判断处理.

group是干嘛的?可以理解成分组的意思,因为各个组件交给各个人维护,所以很容易出现路由同名的情况,比如个人中心组件定义了相册选择页面的路由地址,购物车组件也有相册选择页面的路由地址,所以group的存在是为了做细分。

除了细分的作用以外,还可以减少内存的消耗,Warehouse.routes是一个static的map对象,如果一次性把所有路由信息全存放里面,有可能造成内存溢出。所以Group的第二个作用就是按组加载路由表。

根据group的特性不难理解为什么这里有个递归方法

completion(postcard);   // Reload

不知道有没有人跟我一样疑问,这里不会死循环吗?

public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {if (Warehouse.groupsIndex.containsKey(groupName)){// If this group is included, but it has not been loaded// load this group first, because dynamic route has high priority.Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);Warehouse.groupsIndex.remove(groupName);}// cover old group.if (null != group) {group.loadInto(Warehouse.routes);}}

答案是不会,因为当某个group的路由表信息加载到内存的时候,这个group会从groupsIndex里面删除。

如果RouteMeta不为null,解析uri的相关参数,结合RouteMeta的路由信息设置Postcard的参数。然后调用

_navigation(postcard, requestCode, callback);
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {final Context currentContext = postcard.getContext();switch (postcard.getType()) {case ACTIVITY:// Build intentfinal Intent intent = new Intent(currentContext, postcard.getDestination());intent.putExtras(postcard.getExtras());// Set flags.int flags = postcard.getFlags();if (0 != flags) {intent.setFlags(flags);}// Non activity, need FLAG_ACTIVITY_NEW_TASKif (!(currentContext instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// Set ActionsString action = postcard.getAction();if (!TextUtils.isEmpty(action)) {intent.setAction(action);}// Navigation in main looper.runInMainThread(new Runnable() {@Overridepublic void run() {startActivity(requestCode, currentContext, intent, postcard, callback);}});break;case PROVIDER:return postcard.getProvider();case BOARDCAST:case CONTENT_PROVIDER:case 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;}

最后在上面这个方法里面调用了startActvity方法实现跳转。

整个跳转过程中,离不开路由表,接下来分析下路由表的构建流程,即Warehouse类的初始化工作。拦截器的和provider的相关代码先忽略。

class Warehouse {// Cache route and metasstatic Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();static Map<String, RouteMeta> routes = new HashMap<>();
}

路由表的构建在初始化阶段。_ARouter的init方法会调用LogisticsCenter.init(mContext, executor);方法

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {mContext = context;executor = tpe;try {long startInit = System.currentTimeMillis();//load by plugin firstloadRouterMap();if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {Set<String> routerMap;// It will rebuild router map every times when debuggable.if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// These class was generated by arouter-compiler.routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);if (!routerMap.isEmpty()) {context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();}PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.} else {logger.info(TAG, "Load router map from cache.");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();for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");if (Warehouse.groupsIndex.size() == 0) {logger.error(TAG, "No mapping files were found, check your configuration please!");}if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));}} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}}

我们看到初始化阶段,如果verson非新版本,那么会遍历所有dex文件,找出dex文件里面的ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"的class。

如果已经是新版本了,就从shareprefrence里面读取了缓存,然后根据反射去创建了Class对象,然后将创建好的Group表放到内存中。

这时候又出现新的问题com.alibaba.android.arouter.routes是什么?

从上面的分析,我们知道所有group的class文件都在该package下里面,group的class文件都需要实现IRouteGroup的接口,当我们去看哪些class实现了这些接口的时候,发现都在build目录下。如下图所示。

显而易见,这个class是在编译阶段自动生成的,所以ARouter框架使用了AOP的编程模式。

我们接下来去compiler模块里面看下RouterProcessor类。

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {if (CollectionUtils.isNotEmpty(annotations)) {Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);try {logger.info(">>> Found routes, start... <<<");this.parseRoutes(routeElements);} catch (Exception e) {logger.error(e);}return true;}return false;}

先找到Route注解的Elements,然后解析。

解析的代码比较多,这里分批贴代码分析

//  Follow a sequence, find out metas of group first, generate java file, then statistics them as root.for (Element element : routeElements) {TypeMirror tm = element.asType();Route route = element.getAnnotation(Route.class);RouteMeta routeMeta;// Activity or Fragmentif (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {// Get all fields annotation by @AutowiredMap<String, Integer> paramsType = new HashMap<>();Map<String, Autowired> injectConfig = new HashMap<>();injectParamCollector(element, paramsType, injectConfig);if (types.isSubtype(tm, type_Activity)) {// Activitylogger.info(">>> Found activity route: " + tm.toString() + " <<<");routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);} else {// Fragmentlogger.info(">>> Found fragment route: " + tm.toString() + " <<<");routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);}routeMeta.setInjectConfig(injectConfig);} else if (types.isSubtype(tm, iProvider)) {         // IProviderlogger.info(">>> Found provider route: " + tm.toString() + " <<<");routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);} else if (types.isSubtype(tm, type_Service)) {           // Servicelogger.info(">>> Found service route: " + tm.toString() + " <<<");routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);} else {throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");}categories(routeMeta);}

这里遍历了整个Elements,然后生成对应的RouteMeta。

最后的categories(routeMeta);会对生成的routeMeta做检验,只有合法的routeMeta才会被存入GroupMap里面。path不能为空且path必须是"/"开头才算合法。

然后是通过代码生成java文件,具体生成规则在下面的代码,关键部分补充了注释了。

/** 遍历groupMap **/for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {String groupName = entry.getKey();/** 创建void loadInto(Map<String, RouteMeta> atlas);方法对象 **/MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class)     //注解.addModifiers(PUBLIC)              //方法访问属性.addParameter(groupParamSpec);     //方法参数List<RouteDoc> routeDocList = new ArrayList<>();// Build group method bodySet<RouteMeta> groupData = entry.getValue();/** 遍历每个groupMap下的routeMeta **/for (RouteMeta routeMeta : groupData) {RouteDoc routeDoc = extractDocInfo(routeMeta);ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());switch (routeMeta.getType()) {case PROVIDER:  // Need cache provider's super classList<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();for (TypeMirror tm : interfaces) {routeDoc.addPrototype(tm.toString());if (types.isSameType(tm, iProvider)) {   // Its implements iProvider interface himself.// This interface extend the IProvider, so it can be used for mark providerloadIntoMethodOfProviderBuilder.addStatement("providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",(routeMeta.getRawType()).toString(),routeMetaCn,routeTypeCn,className,routeMeta.getPath(),routeMeta.getGroup());} else if (types.isSubtype(tm, iProvider)) {// This interface extend the IProvider, so it can be used for mark providerloadIntoMethodOfProviderBuilder.addStatement("providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",tm.toString(),    // So stupid, will duplicate only save class name.routeMetaCn,routeTypeCn,className,routeMeta.getPath(),routeMeta.getGroup());}}break;default:break;}// Make map body for paramsTypeStringBuilder mapBodyBuilder = new StringBuilder();Map<String, Integer> paramsType = routeMeta.getParamsType();Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();if (MapUtils.isNotEmpty(paramsType)) {List<RouteDoc.Param> paramList = new ArrayList<>();for (Map.Entry<String, Integer> types : paramsType.entrySet()) {mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");RouteDoc.Param param = new RouteDoc.Param();Autowired injectConfig = injectConfigs.get(types.getKey());param.setKey(types.getKey());param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());param.setDescription(injectConfig.desc());param.setRequired(injectConfig.required());paramList.add(param);}routeDoc.setParams(paramList);}String mapBody = mapBodyBuilder.toString();/*** loadInto方法里面加入具体的代码实现* 比如atlas.put("/module/2", RouteMeta.build(RouteType.ACTIVITY, TestModule2Activity.class, "/module/2", "m2", null, -1, -2147483648));* **/loadIntoMethodOfGroupBuilder.addStatement("atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",routeMeta.getPath(),routeMetaCn,routeTypeCn,className,routeMeta.getPath().toLowerCase(),routeMeta.getGroup().toLowerCase());routeDoc.setClassName(className.toString());routeDocList.add(routeDoc);}// Generate groups/**生成java文件**/String groupFileName = NAME_OF_GROUP + groupName;JavaFile.builder(PACKAGE_OF_GENERATE_FILE,  //指定java文件的包路径TypeSpec.classBuilder(groupFileName)  //java文件名.addJavadoc(WARNING_TIPS)  //java注解.addSuperinterface(ClassName.get(type_IRouteGroup))  //继承的接口.addModifiers(PUBLIC)   //类属性 公有.addMethod(loadIntoMethodOfGroupBuilder.build())  //加入方法.build()).build().writeTo(mFiler);logger.info(">>> Generated group: " + groupName + "<<<");rootMap.put(groupName, groupFileName);  //将组名和java文件名做映射关系docSource.put(groupName, routeDocList);  }

最后在生成root的java文件。

// Write root meta into disk.String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(rootFileName).addJavadoc(WARNING_TIPS).addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT))).addModifiers(PUBLIC).addMethod(loadIntoMethodOfRootBuilder.build()).build()).build().writeTo(mFiler);

AOP阶段路由表的生成规则总结:

1.解析注解得到带有Route注解相关的Elements。

2.遍历所有Elements并解析Route注解的参数,生成对应的RouteMeta数据 ,并插入到groupMap中

3.遍历groupMap,将每个groupName下的所有的RouteMeta生成对应的java代码,然后写入该java文件到内存。此时一个groupName对应一个java文件。

4.并将每个groupName和生成的java文件做映射放在rootMap中。

5.最后再生成对应的root的java文件,该文件主要用于初始化的时候将groupName和java文件的映射关系加载进内存。

到这里,整个页面路由的逻辑都已经理清楚了。

接下来看下依赖注入实现。

AutowiredProcessor类和Autowired注解

@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (CollectionUtils.isNotEmpty(set)) {try {logger.info(">>> Found autowired field, start... <<<");categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));generateHelper();} catch (Exception e) {logger.error(e);}return true;}return false;}
private void categories(Set<? extends Element> elements) throws IllegalAccessException {if (CollectionUtils.isNotEmpty(elements)) {for (Element element : elements) {TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();if (element.getModifiers().contains(Modifier.PRIVATE)) {throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");}if (parentAndChild.containsKey(enclosingElement)) { // Has categriesparentAndChild.get(enclosingElement).add(element);} else {List<Element> childs = new ArrayList<>();childs.add(element);parentAndChild.put(enclosingElement, childs);}}logger.info("categories finished.");}}

编译阶段会去解析Autowired注解,然后获取带有该注解的Elements,然后遍历,如果是私有属性,直接抛异常。然后将正确的elements存到parentAndChild map里面。

接着开始根据parentAndChild的数据里面的内容生成对应的java文件。

生成autowired的java文件步骤和生成ARouter的步骤相似;

1.创建inject方法对象。inject方法在ISyringe接口里面定义了,所以所有autowired类实现该接口。

2.先判断是否是provder,不是provider,然后去区分是activity还是fragment,activity情况下通过getintent去取参数,fragment情况下通过getArguments方法去取参数。

3.然后根据element属性生成对应的get代码,比如boolean对应getBooleanExtra()方法。

4.判断autowired注解里面的required是否是true,如果是true并且是非基本数据类型,则加入对象是否为null的判断代码。

5.此时autowired注解相关的java文件已经生成完毕。

当我们在某个地方调用如下方法的时候:依赖注入才开始生效。

ARouter.getInstance().inject(this);

然后跟下这个方法,会走到这里

static void inject(Object thiz) {AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());if (null != autowiredService) {autowiredService.autowire(thiz);}}

这里用到一个AutowiredService,这个接口继承了IProvider的接口。然后在AutowiredServiceImpl类里面找到了该接口的具体实现。这个实现类也加了Router注解,那么这里有个新的问题,IProvider是干嘛的?

这里我们要回到RouteProcessor看下Provider的解析代码:

switch (routeMeta.getType()) {case PROVIDER:  // Need cache provider's super classList<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();for (TypeMirror tm : interfaces) {routeDoc.addPrototype(tm.toString());if (types.isSameType(tm, iProvider)) {   // Its implements iProvider interface himself.// This interface extend the IProvider, so it can be used for mark providerloadIntoMethodOfProviderBuilder.addStatement("providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",(routeMeta.getRawType()).toString(),routeMetaCn,routeTypeCn,className,routeMeta.getPath(),routeMeta.getGroup());} else if (types.isSubtype(tm, iProvider)) {// This interface extend the IProvider, so it can be used for mark providerloadIntoMethodOfProviderBuilder.addStatement("providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",tm.toString(),    // So stupid, will duplicate only save class name.routeMetaCn,routeTypeCn,className,routeMeta.getPath(),routeMeta.getGroup());}}break;default:break;}

这里看到如果是provider,会生成一段代码,这段代码会将定义的Interface和该interface的实现类做映射,前提条件是,该interface必须继承IProvider。

那做映射干嘛呢?

我们再去看下Provider的映射表使用的地方。也是在wareHouse类里面,发现和路由跳转一样,也是build()方法里面传入string,然后navigation跳转,其实到这里已经能猜到要干嘛了,为了实现跨组件间的方法调用嘛,比如组件A调用组件B里面的Class C的方法,那么就定义一个interface,然后interface下沉到底层组件,Class C继承该interface并实现该方法,接着按照ARouter的provder配置好后,就能在组件A中调用组件B的Class C的暴露的接口了,组件A也不需要去依赖组件B。

这样做得好处就是减少组件间依赖关系。

Provider已经理解了,我们继续AutowiredService分析。

 /*** Recursive injection** @param instance who call me.* @param parent   parent of me.*/private void doInject(Object instance, Class<?> parent) {Class<?> clazz = null == parent ? instance.getClass() : parent;ISyringe syringe = getSyringe(clazz);if (null != syringe) {syringe.inject(instance);}Class<?> superClazz = clazz.getSuperclass();// has parent and its not the class of framework.if (null != superClazz && !superClazz.getName().startsWith("android")) {doInject(instance, superClazz);}}private ISyringe getSyringe(Class<?> clazz) {String className = clazz.getName();try {if (!blackList.contains(className)) {ISyringe syringeHelper = classCache.get(className);if (null == syringeHelper) {  // No cache.syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();}classCache.put(className, syringeHelper);return syringeHelper;}} catch (Exception e) {blackList.add(className);    // This instance need not autowired.}return null;}

这段代码也好理解,获取传入object对象的class对象,通过类名找到编译阶段生成的实现ISyring接口的相关类,反射创建对象,然后调用该对象的inject方法。

至此:依赖注入也分析完了。

接下来看下拦截器

拦截器是什么,参考OKHttp的实现,拦截器就是在处理一个任务的时候,在该任务之前或者之后加入自己的业务逻辑。拦截器设计模式可以有效减少耦合度。

IntercepterProcessor的注解解析其实和前面两个差不多,拦截器表也在Warehouse类里维护。

区别是拦截器提供了手动注册的方法,不一定要通过Aop的方式去注册。

if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}/*** Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(postcard, requestCode, callback);}

这里我们看到如果是绿色通道,则会调用拦截器的方法。

那什么时候是绿色通道呢?

 switch (routeMeta.getType()) {case PROVIDER:  // if the route is provider, should find its instance// Its provider, so it must implement IProviderClass<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();IProvider instance = Warehouse.providers.get(providerMeta);if (null == instance) { // There's no instance of this providerIProvider provider;try {provider = providerMeta.getConstructor().newInstance();provider.init(mContext);Warehouse.providers.put(providerMeta, provider);instance = provider;} catch (Exception e) {logger.error(TAG, "Init provider failed!", e);throw new HandlerException("Init provider failed!");}}postcard.setProvider(instance);postcard.greenChannel();    // Provider should skip all of interceptorsbreak;case FRAGMENT:postcard.greenChannel();    // Fragment needn't interceptorsdefault:break;}

这里我们看到如果是fragment或者provider的时候,打开了绿色通道,所以这两个不会经过拦截器。

然后我们到InterceptorServiceImpl看下拦截器的执行过程

@Overridepublic void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {checkInterceptorsInitStatus();if (!interceptorHasInit) {callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));return;}LogisticsCenter.executor.execute(new Runnable() {@Overridepublic void run() {CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());try {_execute(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((Throwable) postcard.getTag());} else {callback.onContinue(postcard);}} catch (Exception e) {callback.onInterrupt(e);}}});} else {callback.onContinue(postcard);}}

1.校验拦截器是否初始化。

2.开启线程执行拦截器内部代码。

private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {if (index < Warehouse.interceptors.size()) {IInterceptor iInterceptor = Warehouse.interceptors.get(index);iInterceptor.process(postcard, new InterceptorCallback() {@Overridepublic void onContinue(Postcard postcard) {// Last interceptor excute over with no exception.counter.countDown();_execute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.}@Overridepublic void onInterrupt(Throwable exception) {// Last interceptor execute over with fatal exception.postcard.setTag(null == exception ? new HandlerException("No message.") : exception);    // save the exception message for backup.counter.cancel();// Be attention, maybe the thread in callback has been changed,// then the catch block(L207) will be invalid.// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
//                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.
//                        throw new HandlerException(exception.getMessage());
//                    }}});}}

按照从0--n的顺序执行拦截器。

这里拦截器的代码也分析完了。

android ARouter源码分析相关推荐

  1. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我们又有一 个耗时任务需要执行,我们不得不重新创建 ...

  2. Android ADB 源码分析(三)

    前言 之前分析的两篇文章 Android Adb 源码分析(一) 嵌入式Linux:Android root破解原理(二) 写完之后,都没有写到相关的实现代码,这篇文章写下ADB的通信流程的一些细节 ...

  3. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  4. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

    [Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...

  5. 【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序

    [Android SDM660源码分析]- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序 一.创建DXE_DRIVER ...

  6. 【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析

    [Android SDM660源码分析]- 04 - UEFI ABL LinuxLoader 代码分析 1. LinuxLoader.c 系列文章: <[Android SDM660开机流程] ...

  7. Android 音频源码分析——AndroidRecord录音(一)

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

  8. Android框架源码分析——从设计模式角度看 Retrofit 核心源码

    Android框架源码分析--从设计模式角度看 Retrofit 核心源码 Retrofit中用到了许多常见的设计模式:代理模式.外观模式.构建者模式等.我们将从这三种设计模式入手,分析 Retrof ...

  9. 人人网官方Android客户端源码分析(1)

    ContentProvider是不同应用程序之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数据,允许其他应用访问或修改数据;其他应用程序使用ContentRes ...

最新文章

  1. CV01-语义分割笔记和两个模型VGG ResNet的笔记
  2. Linux常用的Shell命令
  3. mysql修改虚拟列属性失败_mysql虚拟列(Generated Columns)及JSON字段类型的使用
  4. Elasticsearch forceMerge操作
  5. python3爬虫(5)百度云盘暴力破解尝试
  6. oracle重建服务器,Oracle重建控制文件的实例教程
  7. 第三方支付处理厂商软件有漏洞,日本美容零售商Acro 10万支付卡信息遭攻击
  8. RF-接口自动化测试-「参数Parameters格式」
  9. golang解决数据库中null值的问题
  10. 现代魔法学院——闲聊哈希表及哈希表的链地址法实现
  11. 2021-10-26 Ubantu练习打字的小游戏
  12. 简单的记录一下使用HAL库的SPI外挂W25Q32
  13. PTA(接口)用java写 7-10 房屋、住宅、写字楼类
  14. 总结恢复百度关键词排名的方法
  15. 王者荣耀服务器维护到什么时候2019,王者荣耀1月2日正式服更新维护公告 2019新年活动上架...
  16. nginx 配置ip_hash不会自动剔除宕机的服务器
  17. 扫雷自定义最难C语言,C语言实现扫雷小游戏(扩展版可选择游戏难度)
  18. 二进制数字电子计算机机器作图软件自动产生的虚拟的大尺度宇宙空间模型
  19. 中台架构与实现(基于DDD和微服务)-读书笔记1
  20. 解决NVIDIA软件或驱动安装包出错

热门文章

  1. 真正的模块化编程原来是这样的!
  2. 一本能改变你人生的好书 - 《富爸爸 - 商学院》
  3. Android 响应用户事件的方法
  4. 音质好的linux主机,5千音质好的HIFI播放器有哪些?5款性价比“神砖”简评
  5. cocos2dx的屏幕适配
  6. 大整数加减法(基础,细节题型)
  7. 【谷歌浏览器 -- Vimium 常用快捷键】
  8. 点阵图、光栅图、位图,向量图
  9. 全球15家大型烟草公司中,大多数都未能采取推进减少烟草危害的措施
  10. 4种方法加密PDF文件