在开始分析ARouter的源码之前,我假定你已经知道ARouter 的用途,并且会熟练使用。下面我在官方demo的基础上,来分析每项功能的流程。
ARouter Github地址
demo 中的 三个 Module
下面三个Module 提供了核心功能
下面两个module 是方便ARouter的使用以及开发
我们在本篇的分析过程中,我们只分析主流程。涉及到类生成,字节码插桩 插入的函数,我们直接查看对应的类即可。它们的生成过程,后续分析
@Route 是 声明路由的注解,主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。
该注解可用于继承了 Activity,Fragment,IProvider(Ioc的接口,下文会介绍)的等子类上,在经过arouter-complier处理后,生成类的命名方式会有所不同
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
//路径URL字符串
String path();
//组名,默认为一级路径名;一旦被设置,跳转时必须赋值
String group() default "";
//该路径的名称,用于产生JavaDoc
String name() default "undefined";
//额外配置的开关信息;譬如某些页面是否需要网络校验、登录校验等
int extras() default Integer.MIN_VALUE;
//该路径的优先级
int priority() default -1;
}
@Interceptor 是拦截器注解,拦截器是全应用全局的。可设置优先级,对所有路由都生效
该注解用于实现IInterceptor 的类上
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}
@Autowired 是给变量自动赋值的。可以在界面跳转的时候参数传递,也可当做依赖注入
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// Mark param's name or service name.
//如果指定的是service名称,就是在当做依赖注入使用
String name() default "";
// If required, app will be crash when value is null.
// Primitive type wont be check!
boolean required() default false;
// Description of the field
String desc() default "";
}
每一条路由,都对应一个RouteMeta对象
public class RouteMeta {
private RouteType type; // 路由的类型:
private Element rawType; // Raw type of route
private Class<?> destination; // 目标Class,最终就是根据这个参数反射得到对应的对象
private String path; // 路径URL
private String group; // 分组,路由都是有分组的,如果没有特别指定,默认第一个项是分组名称
private int priority = -1; // 路由的优先级
private int extra; // 目标Class的额外配置开关信息;譬如某些页面是否需要网络校验、登录校验等
private Map<String, Integer> paramsType; // 目标Class的需要注入的参数 的参数名称:参数类型TypeKind
}
分组的路由表 ——工程名$$Group$$分组名 继承了 IRouteGroup
根路由——工程名$$Root$$模块名 继承了 IRouteRoot
Ioc 路由表——工程名$$Providers$$模块名 继承了 IProviderGroup
拦截器——工程名$$Interceptors$$模块名 继承了 ISyringe
注意,这些自动生成的类,都继承了arouter-api 中的相关接口,下文会分析这些接口在何时被调用
最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
接着是Frossard层,从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template.
再往下一层就完全是SDK的内部实现了,这一层包括了Ware House、Thread、Log、Exception以及Class工具。
Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层
下面就从一个简单的使用,来逐步深入源码
ARouter.init(getApplication());
ARouter 的所有函数的实现,都交给了_ARouter,先初始化_ARouter
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/service/interceptor"
//因为这个流程与界面跳转的流程,基本一致,所以这里暂不分析,可参考4.2 的分析
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
protected static synchronized boolean init(Application application) {
mContext = application;
//初始化LogisticsCenter,就是把编译器生成的那些路由信息加载,此时之后加载root路由、拦截器、provides
LogisticsCenter.init(mContext, executor);
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
LogisticsCenter类
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
//下面会重点分析这个方法
loadRouterMap();
//判断是否由自动生成的代码注册,这里自动生成的代码是指通过arouter-register项目使用 字节码插桩 添加的代码,
//它们的功能是初始化各个module中的root路由、拦截器、provides。
if (registerByPlugin) {
//如果为true,就说明在loadRouterMap()中已经初始化了
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//通过下面的代码初始化
//com.alibaba.android.arouter.routes 路径下的所有类名称会被添加到这个变量中
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// These class was generated by arouter-compiler.
//查找com.alibaba.android.arouter.routes 路径下的所有类(该路径就是第三点分析的routes文件夹)
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 {
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//根据不同的类名,强制转为不同的类型,进行初始化
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);
}
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
arouter-register项目使用 字节码插桩 添加的代码,就是添加到函数loadRouterMap()中,去加载所有module下的com.alibaba.android.arouter.routes的root路由、拦截器、provides,会调用到register函数中
LogisticsCenter类
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());
}
private static void register(String className) {
if (!TextUtils.isEmpty(className)) {
try {
Class<?> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
//上文说过,这些自动生成的类,都继承了atouter-api的相关接口,这里根据接口类型,进行不同的初始化
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
registerInterceptor((IInterceptorGroup) obj);
} else {
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}
} catch (Exception e) {
logger.error(TAG,"register class error:" + className);
}
}
}
这里只分析加载根路由的过程,registerProvider、registerInterceptor过程与它类似
private static void registerRouteRoot(IRouteRoot routeRoot) {
//把registerByPlugin 设置为true
markRegisteredByPlugin();
if (routeRoot != null) {
//调用根路由的loadInto 函数,路由组信息加载到Warehouse.groupsIndex
routeRoot.loadInto(Warehouse.groupsIndex);
}
}
这里以demo中主module的根路由为例
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
// 以路由组名 为key,对应的类class 为值,存入Warehouse.groupsIndex
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}
下面从这行代码,来分析主流程
ARouter.getInstance().build("/test/activity2").navigation();
getInstance 获取ARouter的单例,就不用多说了,直接从build 开始分析
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 这里的navigation 就是根据 by type 获取Provide 的方式
// 下文会介绍 by type 获取Provide 的源码
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
//如果是实现了,这个Provide,就表示需要对路径进行替换
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
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);
}
}
//创建Postcard 对象,一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程
return new Postcard(path, group);
}
}
Postcard类
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}
ARouter类
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
//又调用到_ARouter 中
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//获取预处理的Provide
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果不为空,就执行
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
try {
// 加载本次路由的路由组,并设置关于路由的更多信息到postcard
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//如果没有 对应路由的元数据,就抛出异常,也就是降级处理
if (null != callback) {
//如果设置了,回调,元器件 也是单独降级处理
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
//通过by type 的方式获取DegradeService 实例,进行降级处理,这是全局降级
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//如果在调用 navigation 的时候,设置了回调函数
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
*/
@Override
public void onContinue(Postcard postcard) {
//拦截器的处理结果是继续
_navigation(context, postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
//拦截器的处理结果是中断
//如果在调用 navigation 的时候,设置了回调函数
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
public synchronized static void completion(Postcard postcard) {
...省略代码...
//获取本次路由的元信息,这个信息是在routes文件夹下对应的类中 添加的
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
//如果没有元信息,说明还没有加载,就先把它所在的路由组 所包含的路由信息
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
//获取路由组 元信息
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
...省略报错代码...
} else {
// Load route and cache it into memory, then delete from metas.
try {
//获取路由组的实例
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//加载该路由组下面所有的路由
iGroupInstance.loadInto(Warehouse.routes);
//删除该路由组
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//再次调用该函数,这次调用就不会再走到这里了,因为它的路由组被加载后,该路由肯定不为空
completion(postcard); // Reload
}
} else {
//设置当前路由的class
postcard.setDestination(routeMeta.getDestination());
//设置当前路由的类型,参考 RouteType枚举类
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 @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());
}
switch (routeMeta.getType()) {
//如果是provide类型的,就找到路由对应的class,进行实例化
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
//获取当前路由对应的class
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
//尝试在Warehouse 中获取路由对应的provide 对象
IProvider instance = Warehouse.providers.get(providerMeta);
//获取不到,则通过反射创建新的provide对象
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);
// 这里为本次路由开启了,绿色通道,也就是不经过拦截器
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
// 同上,只有这两个地方,是不经过拦截器
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
LogisticsCenter.completion 执行完,会继续回到4.5 _ARouter#navigation 执行
还记得在 _ARouter.afterInit(); 吗,它自举了 路由 /arouter/service/interceptor,声明该路由的类就是InterceptorServiceImpl
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
//Warehouse.interceptors 中的拦截器,是在初始化时register 函数中设置的
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 {
//执行拦截器,如果拦截器继续,则在内部继续调用_execute
_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.
//在拦截器中断,可传递异常到postcard中
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
//如果没有拦截器,则默认执行 interceptorService.doInterceptions 中拦截器的onContinue
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() {
@Override
public 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.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor excute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); // save the exception message for backup.
counter.cancel();
}
});
}
}
执行完拦截器处理,代码回到 4.5、LogisticsCenter.completion
真正处理路由的函数,是开启activity,还是返回provide的实例,还是开启fragment等
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:
// Build 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);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
//provide实例 已经在LogisticsCenter.completion 中创建好了,这里直接返回
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;
}
至此,处理路由的主要流程就分析完成了,拦截器,provide等的路由处理,和上面类似。下面来看看,参数是如何传递到被启动的activity中的
在被启动的界面,需要使用 ARouter.getInstance().inject(this);
将传递过来的参数,自动注入到本类 使用了@Autowired
注解的变量中
下面就来看看inject 函数
static void inject(Object thiz) {
//通过路由获取/arouter/service/autowired 的实例,AutowiredServiceImpl,这个也是在arouter-api中实现的
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
到AutowiredServiceImpl类中,查看autowire 函数
@Override
public void autowire(Object instance) {
//获取被启动类的实例
String className = instance.getClass().getName();
try {
//检查是否在黑名单中
if (!blackList.contains(className)) {
//先尝试在缓存中获取,获取到的对象是,arouter-complier 对Autowired 注解处理 生成的类
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
//调用该实例的inject ,例如 Test2Activity$$ARouter$$Autowired 中的inject
autowiredHelper.inject(instance);
//加入缓存
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
//如果 注解发生异常,就把该类 加入黑名单
blackList.add(className); // This instance need not autowired.
}
}
下面来看看Test2Activity$$ARouter$$Autowired的具体实现
public class Test2Activity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
//通过by type的方式,获取一个可以处理json的对象
serializationService = ARouter.getInstance().navigation(SerializationService.class);
Test2Activity substitute = (Test2Activity)target;
//为被启动类 的变量赋值,这样就实现了Ioc的功能
substitute.key1 = substitute.getIntent().getExtras() == null ? substitute.key1 : substitute.getIntent().getExtras().getString("key1", substitute.key1);
}
}
下面我们来看看,如何通过by type 来获取provide实例
通过type的方式获取provide 是这样使用的ARouter.getInstance().navigation(SerializationService.class);
也是navigation 函数,可以看到传入参数不同
最终调用到_ARouter 的navigation中
protected <T> T navigation(Class<? extends T> service) {
try {
//先在Warehouse.providersIndex 中获取对应类型的路由元数据
//Warehouse.providersIndex的数据 初始化时在registerProvider 中添加的,
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);
//最后返回provide对象
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
通过字节码插桩的方式实现,如果不使用这个也是可以的,流程走的就是 4.1、初始化 时,LogisticsCenter init函数中的else 语句,它们的效果是一样的,只是加固可能会出现问题,
下面是官方的描述
可选使用,通过 ARouter 提供的注册插件进行路由表的自动加载(power by AutoRegister), 默认通过扫描 dex
的方式 进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问 dex
文件,初始化失败的问题,需要注意的是,该插件必须搭配 api 1.3.0 以上版本使用!
至此,ARouter的主要功能流程,就分析完了