Android学习之路(22) ARouter原理解析

1.ARouter认知

首先我们从命名来看:ARouter翻译过来就是一个路由器

官方定义

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

那么什么是路由呢? 简单理解就是:一个公共平台转发系统

工作方式:

arouter原理.png

  • 1.注册服务:将我们需要对外暴露的页面或者服务注册到ARouter公共平台中
  • 2.调用服务:调用ARouter的接口,传入地址和参数,ARouter解析传入的地址和参数转发到对应的服务中

通过ARouter形成了一个无接触解耦的调用过程

2.ARouter架构解析

我们来看下ARouter的源码架构:

ARouter源码架构.png

  • app:是ARouter提供的一个测试Demo
  • arouter-annotation:这个lib模块中声明了很多注解信息和一些枚举类
  • arouter-api:ARouter的核心api,转换过程的核心操作都在这个模块里面
  • arouter-compiler:APT处理器,自动生成路由表的过程就是在这里面实现的
  • arouter-gradle-plugin:这是一个编译期使用的Plugin插件,主要作用是用于编译器自动加载路由表,节省应用的启动时间。

3.原理讲解

这里我们不会一开始就大篇幅对源码进行讲解: 我们先来介绍ARouter中的几个重要概念:有了这几个概念,后面在去看源码就会轻松多了

前置基础概念:

概念1:PostCard(明信片)

既然是明信片要将信件寄到目的人的手上就至少需要:收件人的姓名和地址,寄件人以及电话和地址等

ARouter就是使用PostCard这个类来存储寄件人和收件人信息的。

java
复制代码
public final class Postcard extends RouteMeta {// Baseprivate Uri uri; //如果使用Uri方式发起luyouprivate Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!private Bundle mBundle;         // 需要传递的参数使用bundle存储private int flags = 0;         // 启动Activity的标志:如NEW_FALGprivate int timeout = 300;      // 路由超时private IProvider provider;     // 使用IProvider的方式跳转private boolean greenChannel;	//绿色通道,可以不经过拦截器private SerializationService serializationService; //序列化服务serializationService:需要传递Object自定义类型对象,就需要实现这个服务private Context context;        // May application or activity, check instance type before use it.private String action;			//Activity跳转的Action// Animationprivate Bundle optionsCompat;    // The transition animation of activityprivate int enterAnim = -1;private int exitAnim = -1;...
}

PostCard继承了RouteMeta

java
复制代码
public class RouteMeta {private RouteType type;         // 路由类型:如Activity,Fragment,Provider等private Element rawType;        // 路由原始类型,在编译时用来判断private Class<?> destination;   // 目的Class对象private String path;            // 路由注册的pathprivate String group;           // 路由注册的group分组private int priority = -1;      // 路由执行优先级,priority越低,优先级越高,这个一般在拦截器中使用private int extra;              // Extra dataprivate Map<String, Integer> paramsType;  //  参数类型,例如activity中使用@Autowired的参数类型private String name; //路由名字,用于生成javadocprivate Map<String, Autowired> injectConfig;  // 参数配置(对应paramsType).}

RouteMeta:主要存储的是一些目的对象的信息,这些对象是在路由注册的时候才会生成。

概念2:Interceptor拦截器

了解OkHttp的都知道,其内部调用过程就是使用的拦截器模式,每个拦截器执行的对应的任务。

ARouter中也是如此,所有的路由调用过程在到达目的地前都会先经过自定义的一系列拦截器,实现一些AOP切面编程。

java
复制代码
public interface IInterceptor extends IProvider {/*** The operation of this interceptor.** @param postcard meta* @param callback cb*/void process(Postcard postcard, InterceptorCallback callback);
}

IInterceptor是一个接口,继承了IProvider,所以其也是一个服务类型

只需要实现process方法就可以实现拦截操作。

概念3:greenChannel:绿色通道

设置了绿色通道的跳转过程,可以不经过拦截器

概念4:Warehouse:路由仓库

Warehouse意为仓库,用于存放被 @Route、@Interceptor注释的 路由相关的信息,也就是我们关注的destination等信息

举个例子:

moduleB发起路由跳转到moduleA的activity,moduleB没有依赖moduleA,只是在moduleA的activity上增加了@Route注解。 由于进行activity跳转需要目标Activity的class对象来构建intent,所以必须有一个中间人,把路径"/test/activity"翻译成Activity的class对象,然后moduleB才能实现跳转。(因此在ARouter的使用中 moduleA、moduleB 都是需要依赖 arouter-api的)

这个中间人那就是ARouter了,而这个翻译工所作用到的词典就是Warehouse,它存着所有路由信息。

java
复制代码
class Warehouse {//所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级//(IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); //所有路由元信息,是在completion中赋值,key是path//首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务static Map<String, RouteMeta> routes = new HashMap<>();//所有服务provider实例,在completion中赋值,key是IProvider实现类的classstatic Map<Class, IProvider> providers = new HashMap<>();//所有provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。//主要用于使用IProvider实现类的class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)static Map<String, RouteMeta> providersIndex = new HashMap<>();//所有拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");//所有拦截器实例,是在ARouter初始化完成后立即创建static List<IInterceptor> interceptors = new ArrayList<>();
...
}

Warehouse存了哪些信息呢?

  • groupsIndex:存储所有路由组元信息:

1661149556811.jpg

js
复制代码
key:group的名称
value:路由组的模块类class类:
赋值时机:初始化的时候
  • routes:存储所有路由元信息。切记和上面路由组分开,路由是单个路由,路由组是一批次路由

routes.png

js
复制代码
key:路由的path
value:路由元信息
赋值时机:LogisticsCenter.completion中赋值
备注:首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务
  • providers:存储所有服务provider实例。
js
复制代码
key:IProvider实现类的class
value:IProvider实例
赋值时机:在LogisticsCenter.completion中赋值
  • providersIndex:存储所有provider服务元信息(实现类的class对象)。

provider.png

js
复制代码
key:IProvider实现类的全类名
value:provider服务元信息
赋值时机:ARouter初始化中赋值。
备注:用于使用IProvider实现类class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
  • interceptorsIndex:存储所有拦截器实现类class对象。

Interceptor.png

js
复制代码
key:优先级
value:所有拦截器实现类class对象
赋值时机:是在ARouter初始化时收集到
  • interceptors,所有拦截器实例。是在ARouter初始化完成后立即创建

其中groupsIndex、providersIndex、interceptorsIndex是ARouter初始化时就准备好的基础信息,为业务中随时发起路由操作(Activity跳转、服务获取、拦截器处理)做好准备。

概念5:APT注解处理器

ARouter使用注解处理器,自动生成路由帮助类: 我们使用ARouter编译后,会在对应模块下自动生成以下类: 这些类的生成规则都是通过APT在编译器自动生成的,关于APT在ARouter中的使用方式,后面会单独拿一节出来讲解:

  • Android开源系列-组件化框架Arouter-(三)APT技术详解
概念6:AGP插件

ARouter使用了一个可选插件:“com.alibaba:arouter-register:1.0.2” 使用这个插件可以在编译器在包中自动检测以及加载路由表信息,而不需要在运行启动阶段再使用包名去dex文件中加载,提高app启动效率 关于这块的,后面会在:

  • Android开源系列-组件化框架Arouter-(四)AGP插件详解

有了以上几个概念做基础现在我们再到源码中去看看ARouter是如何跨模块运行起来的

源码分析:

首先我们来看路由过程:

  • 步骤1:初始化ARouter
js
复制代码
ARouter.init(this)
  • 步骤2:注册Activity路由
js
复制代码
@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {@Autowiredint age = 10;protected void onCreate(Bundle savedInstanceState) {ARouter.getInstance().inject(this);}
}
  • 步骤3:通过path启动对应的Activity
js
复制代码
ARouter.getInstance().build("/test/activity2").navigation();

下面我们分别来分析以上过程:

步骤1分析:ARouter.init(this)
java
复制代码
/*** 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方法,进入看看

java
复制代码
protected static synchronized boolean init(Application application) {mContext = application;LogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;
}

内部初始化了一些mContext,mHandler以及字段信息 最重要的是LogisticsCenter.init(mContext, executor):这句 进入看看:

java
复制代码
/*** LogisticsCenter init, load all metas in memory. Demand initialization*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {try {//使用AGP插件进行路由表的自动加载loadRouterMap();//如果registerByPlugin被设置为true,说明使用的是插件加载,直接跳过if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {//如果是false,则调用下面步骤加载Set<String> routerMap;// 如果是debug模式或者是新版本的,则每次都会去加载routerMap,这会是一个耗时操作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>()));}//这里循环获取routerMap中的信息for (String className : routerMap) {//如果className = "com.alibaba.android.arouter.routes.ARouter$$Root"格式,则将路由组信息添加到Warehouse.groupsIndex中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);//如果className = "com.alibaba.android.arouter.routes.ARouter$$Interceptors"格式,则将拦截器信息添加到Warehouse.interceptorsIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);//如果className = "com.alibaba.android.arouter.routes.ARouter$$Providers"格式,则将服务Provider信息添加到Warehouse.providersIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}
}

总结_ARouter的init操作:

  • 1.优先使用插件加载路由表信息到仓库中,如果没有使用插件,则使用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类对象 查找到后,保存到sp文件中,非debug或者新版本的情况下,下次就直接使用sp文件中缓存的类信息即可。
  • 2.查找到对应的类文件后,使用反射调用对应的类的loadInto方法,将路由组,拦截器以及服务Provider信息加载到Warehouse仓库中

继续看init方法中给的_ARouter.afterInit

java
复制代码
static void afterInit() {// Trigger interceptor init, use byName.interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

找到/arouter/service/interceptor注解处

java
复制代码
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService 

这里给ARouter创建了一个InterceptorServiceImpl服务的实例对象,后面讲到拦截器的时候会用到

步骤2分析:注册Activity路由

我们注册的Activity,Provider等路由信息,会在编译器被注解处理器处理后生成对应的路由表:

路由表在步骤1中ARouter初始化的时候被加载到Warehouse

步骤3分析:通过path启动对应的Activity
java
复制代码
ARouter.getInstance().build("/test/activity2").navigation();

这里我们拆分成三个部分:getInstancebuildnavigation

  • 3.1:getInstance
java
复制代码
public static ARouter getInstance() {if (!hasInit) {throw new InitException("ARouter::Init::Invoke init(context) first!");} else {if (instance == null) {synchronized (ARouter.class) {if (instance == null) {instance = new ARouter();}}}return instance;}
}

做了init检查并创建了一个ARouter对象

  • 3.2:build
java
复制代码
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), true);}
}
1.使用PathReplaceService,可以替换原path为新的path
继续看build方法:
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);}
}

看到这里创建了一个Postcard,传入path和group,对Postcard前面有讲解,这里不再重复

  • 3.3:navigation

最后会走到_ARouter中的同名navigation方法中:

java
复制代码
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;}try {//完善PostCard信息 留个点1LogisticsCenter.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();}});}//没有找到路由信息,则直接返回callback.onLostif (null != callback) {callback.onLost(postcard);} else {// 没有callback则调用全局降级服务DegradeService的onLost方法DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);if (null != degradeService) {degradeService.onLost(context, postcard);}}return null;}//回调callback.onFound提醒用户已经找到pathif (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 {//绿色通道直接调用_navigationreturn _navigation(postcard, requestCode, callback);}return null;
}

方法任务:

  • 1.预处理服务
  • 2.完善PostCard信息
  • 3.如果是非绿色通道,则使用拦截器处理请求
  • 4.调用_navigation处理

这里我们看下第3点:拦截器处理

java
复制代码
interceptorService.doInterceptions{public void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}public void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}}
}

如果被拦截回调callback.onInterrupt 如果没有就执行_navigation方法

进入interceptorService.doInterceptions看下:

前面分析过interceptorService是InterceptorServiceImpl对象

java
复制代码
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {private static boolean interceptorHasInit;private static final Object interceptorInitLock = new Object();@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计数器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 {//继续执行下面任务onContinuecallback.onContinue(postcard);}} catch (Exception e) {callback.onInterrupt(e);}}});} else {callback.onContinue(postcard);}}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执行拦截器_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());
//                    }}});}}
}

拦截器总结:

  • 1.使用计数器对拦截器技术,执行开始计数器+1,执行结束计数器-1,如果拦截器执行时间到,计数器数大于0,则说明还有未执行完成的拦截器,这个时候就超时了退出
  • 2.拦截器执行使用递归的方式进行
  • 3.拦截器执行完成继续执行_navigation方法

我们来看_navigation方法:

java
复制代码
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;
}

这个方法其实就是根据PostCardtype来处理不同的请求了

  • 1.Activity,直接跳转
  • 2.Fragment,Provider,BroadcaseReceiver和ContentProvider,直接返回类的实例对象。

整个过程我们就基本了解了。 上面还留了一个点:

留的点1ARouter是如何完善PostCard信息

看LogisticsCenter.completion(postcard);

进入这个方法:

java
复制代码
public synchronized static void completion(Postcard postcard) {if (null == postcard) {throw new NoRouteFoundException(TAG + "No postcard!");}//去Warehouse.routes去取路由元数据,开始肯定是没有的RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());//没获取到if (null == routeMeta) {// Maybe its does't exist, or didn't load.//判断Warehouse.groupsIndex路由组中是否有这个groupif (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");} else {try {//动态添加路由元信息到路由中addRouteGroupDynamic(postcard.getGroup(), null);} catch (Exception e) {              }//重新加载。这个时候就会有路由元信息了completion(postcard);   // Reload}} else {//给postcard设置目的地,设置类型,设置优先级,设置Extra等信息postcard.setDestination(routeMeta.getDestination());postcard.setType(routeMeta.getType());postcard.setPriority(routeMeta.getPriority());postcard.setExtra(routeMeta.getExtra());Uri rawUri = postcard.getUri();...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;}}}进入addRouteGroupDynamic
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去groupsIndex获取对应的组的class对象,然后调用class对象的loadInto方法,将路由元数据加载到Warehouse.routes 然后重新调用completion完善方法去Warehouse.routes中取出路由信息并加载到PostCard中,这样PostCard中就获取到了目的地址信息。

下面我画了一张图描述了上面的调用过程 一图胜千言

arouter调用过程.png

总结

本文先介绍了ARouter使用过程中 的一些基本概念,理解了这些概念后,我们再从使用步骤触发,对每个使用节点进行了介绍。 最后使用一张图总结了整个使用原理过程: 这里我们还有一些悬念:

  • 1.ARouter帮助类是如何生成的,这里使用到了APT注解处理器的技术 关于APT我们会在下一章:

Android开源系列-组件化框架Arouter-(三)APT技术详解

  • 这里还有个有趣的现象,我们在调用路由表加载的时候: 使用了loadRouterMap加载,但是查看里面代码:
java
复制代码
private static void loadRouterMap() {registerByPlugin = false;// auto generate register code by gradle plugin: arouter-auto-register// looks like below:// registerRouteRoot(new ARouter..Root..modulejava());// registerRouteRoot(new ARouter..Root..modulekotlin());
}

居然是空的。。 呃呃呃 没关系看注解:

java
复制代码
auto generate register code by gradle plugin: arouter-auto-register

可以看到这里使用了arouter-auto-register插件中自动生成注册代码的方式: 这里其实就是使用到了字节码插庄技术,动态添加了代码,这里留到:

Android开源系列-组件化框架Arouter-(四)AGP插件详解

好了,本篇就到这里了。

作者:小余的自习室
链接:https://juejin.cn/post/7134632360087126023
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/422160.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.12、中间人攻击(ARP欺骗)

一、ARP协议原理 地址解析协议(Address Resolution Protocol&#xff0c;ARP)&#xff0c;负责把目的主机的IP 地址解析成目的MAC地址&#xff0c;地址解析的目标就是发现逻辑地址与物理地址的映射关系。网络中的计算机、交换机、路由器等都会定期维护自己的ARP缓存表。 为什么…

ELK分离式日志(2)

目录 一.FilebeatELK 部署 开台服务器&#xff08;192.168.233.50&#xff09;下载fliebeat&#xff1a; 安装nginx后查看下日志文件&#xff1a; 设置 filebeat 的主配置文件: 关闭logstash&#xff0c;检测文件&#xff1a; 在50节点上启动filebeat&#xff1a; 访问页…

2、Line Charts折线图

可视化时间趋势 现在你已经熟悉了编码环境,是时候学习如何制作自己的图表了! 在本教程中,您将学习足够的Python来创建专业外观的折线图。然后,在接下来的练习中,您将使用您的最新技能处理真实世界的数据集。 本课程数据集夸克网盘下载链接:https://pan.quark.cn/s/a235ac…

Windows下RocketMQ搭建

RocketMQ安装 注&#xff1a;Windows必须先安装64bit的 JDK1.8 或以上版本及Maven 。 1.官网下载&#xff1a;下载 | RocketMQ 2.将下载下的安装文件解压到本地磁盘 3.配置环境变量 &#xff1a; 变量名&#xff1a;ROCKETMQ_HOME 变量值&#xff1a;G:\RocketMQ\rocketmq…

HCIA NAT练习

目录 实验拓扑 实验要求 实验步骤 1、IP分配 2、使用ACL使PC访问外网 3、缺省路由 4、边界路由器公网ip端口配置 测试 实验拓扑 实验要求 1、R2为ISP路由器&#xff0c;其上只能配置ip地址&#xff0c;不得再进行其他的任何配置 2、PC1-PC2可以ping通客户平板和DNS服…

[视频处理]关于视频处理的多画面样式

在开发视频系统时&#xff0c;经常会遇到多画面的需求&#xff0c;这里收集一些多画面的素材&#xff0c;共大家参考。 图片来源于网络&#xff0c;仅供参考。 后续补充文章 【图像处理】使用FPGA实现视频多画面的方案 多画面样式

springboot集成easypoi

easypoi,主打的功能就是容易,通过简单的配置&#xff0c;就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出 pom导入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-star…

【C++】priority_queue模拟实现过程中值得注意的点

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 本篇文章旨在记录博主在模…

【差分数组】【图论】【分类讨论】【整除以2】100213按距离统计房屋对数目

作者推荐 【动态规划】【数学】【C算法】18赛车 本文涉及知识点 差分数组 图论 分类讨论 整除以2 LeetCode100213按距离统计房屋对数目 给你三个 正整数 n 、x 和 y 。 在城市中&#xff0c;存在编号从 1 到 n 的房屋&#xff0c;由 n 条街道相连。对所有 1 < i < n…

vivado JTAG链、连接、IP关联规则

JTAG链 这列出了定义板上可用的不同JTAG链。每个链都列在下面<jtag_chain>以及链的名称&#xff0c;以及定义名称和链中组件的位置&#xff1a; <jtag_chains> <jtag_chain name"chain1"> <position name"0" component"part0…

基于ChatGPT4+Python近红外光谱数据分析及机器学习与深度学习建模教程

详情点击链接&#xff1a;基于ChatGPT4Python近红外光谱数据分析及机器学习与深度学习建模教程 第一&#xff1a;GPT4 1、ChatGPT&#xff08;GPT-1、GPT-2、GPT-3、GPT-3.5、GPT-4模型的演变&#xff09; 2、ChatGPT对话初体验 3、GPT-4与GPT-3.5的区别&#xff0c;以及与…

使用WAF防御网络上的隐蔽威胁之目录穿越

目录穿越&#xff08;Directory Traversal&#xff09;是一种网络安全攻击手段&#xff0c;也被称为路径穿越。 这种攻击允许攻击者访问存储在Web服务器文件系统上的文件和目录&#xff0c;这些文件和目录原本不应该对用户可见或可访问。 通过利用安全漏洞&#xff0c;攻击者…