继续上一篇的Arouter讲解说起,上一篇主要介绍了Arouter使用,这篇主要围绕Arouter源码的设计,以及通过源码我们能学习到什么,以及如何应对面试过程中Arouter的问题
目的
- 源码分析
- Arouter路由跳转的设计
- Arouter拦截器的设计
- Arouter的服务怎么设计的
- Arouter的注解属性怎么获取的
- Arouter自动加载路由表
- 思考源码的设计
源码分析
在使用Arouter过程中,我们的module依赖了
arouter-api
、arouter-compiler
,在编译期arouter-compiler
通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件,大家如果想学习学习编译期扫描注解生成class文件可以学习apt
相关的技术,或者看Arouter官网的arouter-compiler
模块怎么扫描注解生成class文件的。
比如我在login模块的LoginActivity
中定义如下的注解:
@Route(path = "/login/loginActivity")
class LoginActivity : AppCompatActivity() {
}
结果在module的build目录下生成了ARouter$$Root$$login
class文件,它是实现IRouteRoot
接口:
public class ARouter$$Root$$login implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("login", ARouter$$Group$$login.class);
}
}
而此处的ARouter$$Group$$login
类它是实现了IRouteGroup
接口:
public class ARouter$$Group$$login implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/login/loginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, -1, -2147483648));
}
}
里面把loginActivity的信息通过RouteMeta
存到了传进来的map中了。
其实我们的ARouter$$Group$$组名
类不只是存放了activity的RouteMeta
信息,还会有IProvider类型。
上一节我们使用的JsonServiceImpl
它是实现了SerializationService
接口,最终是实现了IProvider
接口,和activity使用的注解是一样,只不过用的group是yourservicegroupname
,还有DegradeServiceImpl
类也是一样的,它是实现了DegradeService
接口,最终是实现了IProvider
接口,group也是yourservicegroupname
,都会在ARouter$$Group$$yourservicegroupname
类中保存了一个RouteMeta
信息;如果用Interceptor
注解的话,会生成对应的ARouter$$Interceptors$$模块名
的class类,上一节定义的LoginInterceptor
最终是被ARouter$$Interceptors$$app
管理的:
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(8, LoginInterceptor.class);
}
}
IInterceptor就没有被包装成RouteMeta
对象,上面在ARouter$$Group$$yourservicegroupname
中定义的IProvider信息还会在ARouter$$Providers$$app
被定义了一遍:
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/yourservicegroupname/DegradeServiceImpl", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl.class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
}
}
其实都是我们在app模块中定义的IProvider类型的service,那为什么在ARouter$$Group$$组名
定义了IProvider类型,那为什么还需要在ARouter$$Providers$$模块名
中还要定义一遍呢,看官莫急,听我细细道来。
小节
所以在apt阶段,生成的class类有
Arouter$$Root$$模块名
,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName()}")
属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/login/loginActivity,会认为组名是login,Arouter$$Root$$组名
放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名
下面。
初始化
我们在上一面使用部分,初始化部分代码如下:
ARouter.init(this)
就一句,那下面追随源码看下:
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.afterInit方法,其实我们的入口虽然是ARouter类的,但是真正调用的还是_ARouter类的方法,废话不多说,直接看_ARouter.init方法:
_ARouter.init
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;
}
最终还是调用了LogisticsCenter
的init方法,并且拿到主线的looper给了mHandler
,顺着看LogisticsCenter
的init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
//如果通过插件获取路由表信息,则该方法registerByPlugin=false
loadRouterMap();
if (registerByPlugin) {
} else {
Set<String> routerMap;
// 如果是debuggable=true或者有新的版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 加载前缀为com.alibaba.android.arouter.routes的class类,放到set集合里面
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//将过滤到的class类放到sp中,方便下次直接取
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 {
//直接从sp中取过滤到的class类
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)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到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() + "]");
}
}
在上面init方法中做了几件事:
- 通过
loadRouterMap
方法判断是不是通过arouter-register
自动加载路由表,如果是通过自动加载的则registerByPlugin=true
,这里我们先不管通过arouter-register
自动加载的方式, - 紧接着通过
ClassUtils.getFileNameByPackageName
(此处用到了线程池、CountDownLatch
面试高频考点)获取到apk中前缀为com.alibaba.android.arouter.routes
的类,这里面主要是通过判断是不是支持MultiDex,如果不支持MultiDex,扫描所有的dex文件,然后压缩成zip文件,然后通过DexFile.loadDex
转化成DexFile对象,如果支持MultiDex,直接new DexFile
,然后循环DexFile拿里面的class文件,然后过滤出com.alibaba.android.arouter.routes
前缀的class并返回。 - 拿到了需要的class类后,放到sp里面,方便下次不去扫描apk拿class,更新版本号
- 将
com.alibaba.android.arouter.routes.ARouter$$Root
前缀的class类放到Warehouse.groupsIndex
中 - 将
com.alibaba.android.arouter.routes.ARouter$$Interceptors
前缀的class类放到Warehouse.interceptorsIndex
中 - 将
com.alibaba.android.arouter.routes.ARouter$$Providers
前缀的class类放到Warehouse.providersIndex
中
_ARouter.afterInit
该方法里面会去拿InterceptorServiceImpl
,怎么去拿的,以及里面做了些啥,我们一一来看:
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
ARouter.build
ARouter.getInstance().build("/arouter/service/interceptor")
会返回PostCard
对象,并给PostCard
的group
和path
属性赋值为arouter
、/arouter/service/interceptor
:
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);
}
}
先判断有没有设置PathReplaceService
类型的路由,如果有会调用forString
方法返回新的path,这也就是我们的path替换的IProvider
,在第一篇文章我们定义过PathReplaceServiceImpl
类:
@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
class PathReplaceServiceImpl : PathReplaceService {
override fun forString(path: String): String {
if (path == "/login/loginActivity") {
return "/share/shareActivity"
}
return path // 按照一定的规则处理之后返回处理后的结果
}
override fun forUri(uri: Uri?): Uri? {
return null // 按照一定的规则处理之后返回处理后的结果
}
override fun init(context: Context?) {
}
}
如果有该PathReplaceService
会替换掉我们的/login/loginActivity
为/share/shareActivity
,这里先不管怎么拿到PathReplaceServiceImpl
,通过extractGroup
方法拿到group:
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;
}
}
很简单,按照第一个'/'和第二个'/'的字符作为group信息,所以这也是为什么我们在定义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);
}
}
所以build过程把传过来的path构造出Postcard
对象,给path和group赋值。
PostCard.navigation
navigation有很多重载的方法,最终都会走_Arouter.navigation,其中navigation里面也有两种形式获取到路由表类,我们先介绍activity常规的形式:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//这也是个路由表,通过另外一种形式获取PretreatmentService的实例
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果onPretreatment返回false就是自己处理路由逻辑,不往下走了
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
return null;
}
try {
//最终会走这里
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
...
if (null != callback) {
callback.onLost(postcard);
} else {
// 获取DegradeService的实例
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//省略provider部分逻辑和_navigation部分代码
return null;
}
由于navigation代码比较长,我把代码分块来说,上面首先获取PretreatmentService
类型的路由表,我们先只说上面传入/arouter/service/interceptor
,怎么得到InterceptorService
实例的,我们直接看LogisticsCenter.completion
:
public synchronized static void completion(Postcard postcard) {
//第一次进来是拿不到RouteMeta信息的,因为routes是空的
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//我们传过来的postcard的group是arouter、path是/arouter/service/interceptor
//我们在groupIndex中找对应的groupMeta,其实看到这的时候,我们默认是没有root为arouter的组,只能去arouter默认提供的root中找
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
} else {
try {
//反射拿到ARouter$$Group$$arouter
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//所以最终把InterceptorServiceImpl放到了Warehouse.routes中
iGroupInstance.loadInto(Warehouse.routes);
//用完groupsIndex对应的IRouteGroup信息后,从map中移除掉,下次就直接从routes中去拿了
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//继续走一遍completion,下次会走下面的else
completion(postcard);
}
} else {
//对postCard属性赋值
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
//默认uri为空
if (null != rawUri) {
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
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()) {
//由于InterceptorServiceImpl是provider类型的
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
//拿对应的provider
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try {
//反射创建InterceptorServiceImpl
provider = providerMeta.getConstructor().newInstance();
//调用InterceptorServiceImpl的init方法
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
//给postcard赋值
postcard.setProvider(instance);
postcard.greenChannel();
break;
case FRAGMENT:
postcard.greenChannel();
default:
break;
}
}
}
上面代码还是很清晰的,首先从Warehouse.routes
去拿对应的RouteMeta
信息,如果没有,就先去Warehouse.groupsIndex
中拿,而此时的postCard的group是arouter,我们从下面图看下,拿到的是ARouter$$Group$$arouter
的class:
所以此时的iGroupInstance是
ARouter$$Group$$arouter
,通过反射创建ARouter$$Group$$arouter
,紧接着把Warehouse.routes
传进它的loadInto方法:
所以我们最终能确定把
AutowiredServiceImpl
和InterceptorServiceImpl
的RouteMeta
放进了Warehouse.routes
的map中。注意:上面用完了Warehouse.groupsIndex中对应的group信息后,会从Warehouse.groupsIndex中移除该group的信息
最后又走一遍completion
方法,所以会走else分支,由于我们还在获取InterceptorServiceImpl
过程中,它的RouteType=PROVIDER
,所以providerMeta
是InterceptorServiceImpl
类型的,然后去Warehouse.providers
拿,此时是空的,所以通过反射创建InterceptorServiceImpl
对象,创建完调用InterceptorServiceImpl
调用init方法:
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
//遍历我们自己代码里面定义的interceptorsIndex
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
//拿到对应的iInterceptor后
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
//调用init
iInterceptor.init(context);
//把iInterceptor放到interceptors中
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
上面获取interceptorsIndex
,还记得上一节我们定义的LoginInterceptor
吗?它会放到interceptorsIndex的map里面,所以这里是拿到所有的IInterceptor
,反射创建每一个IInterceptor
,调用init方法,添加到Warehouse.interceptors
中。
到这里,我们已经清楚了
Warehouse.routes
存放了AutowiredServiceImpl
和InterceptorServiceImpl
类型的RouteMeta
,Warehouse.providers
存放了InterceptorServiceImpl
,Warehouse.interceptors
存放了所有的IInterceptor
。
创建完InterceptorServiceImpl
,我们一下子要回到_ARouter
的navigation
方法的下半部分代码,上面没有贴出代码:
//如果是`InterceptorServiceImpl`类型的postcard.isGreenChannel()是true,除非是activity或fragment类型的
if (!postcard.isGreenChannel()) {
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);
}
所以上面代码我们直接看_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 PROVIDER:
return postcard.getProvider();
}
return null;
}
这里先把其他类型给省略掉了,如果是PROVIDER
类型的,直接把postCard的provider直接返回,所以我们上面怎么拿到的InterceptorServiceImpl
就迎刃而解了。
PostCard. T navigation(Class<? extends T> service)
上面分析的是通过path获取到路由表的实例,还有另外通过传进来的class类型也可以获取,我们就拿上一节用到的JsonServiceImpl
怎么拿到的,在PostCard中可以通过withObject
传值,其实归功于JsonServiceImpl
,可以看下面代码:
public Postcard withObject(@Nullable String key, @Nullable Object value) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
mBundle.putString(key, serializationService.object2Json(value));
return this;
}
所以直接看_Arouter.navigation(Class<? extends T> service) 方法:
protected <T> T navigation(Class<? extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
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方法:
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());
}
}
此处是从providersIndex
中去拿对应的RouteMeta
信息,而providersIndex
是在初始化sdk中通过加载ARouter$$Providers$$模块名
的loadInto方法,好吧,为了大家能顺着阅读,我把上面的代码重新复制了一份:
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/yourservicegroupname/DegradeServiceImpl", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl.class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
}
}
看到了没,SerializationService
作为的key时候,指向的是JsonServiceImpl
的RouteMeta
,所以在
navigation
方法传class对象的时候,是在providersIndex
中先去找,没找到,最终通过navigation传path去找,这也是为什么在ARouter$$Group$$组名
和ARouter$$Providers$$模块名
中都定义了provider的routeMeta信息,一种通过path来找provider,一种通过class来找provider
Arouter路由跳转的设计
由于activity路由的postCard的isGreenChannel()为false,因此在_Arouter.navigation方法中会走如下代码:
上面已经分析了
interceptorService
是interceptorServiceImpl
,因此看doInterceptions方法:
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
//省略了拦截器的流程,直接看callback.onContinue
callback.onContinue(postcard);
}
如果默认没有拦截器,直接会走callback.onContinue
,回到_Arouter的navigation,会走_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:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
}
return null;
}
所以最终也是通过routeMeta的destination作为目标activity的class跳转。
所以activity最终跳转也是先获取routeMeta,最终完成跳转。
Arouter拦截器的设计
这个我们直接看InterceptorServiceImpl
的doInterceptions
方法:
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
//如果有interceptors
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(0, interceptorCounter, postcard);
//拦截器如果超时会回调callback.onInterrupt
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) {
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) {
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
}
}
创建CancelableCountDownLatch
作为所有拦截器处理完成的标志,处理IInterceptor
通过触发process方法,如果想继续处理下一个拦截通过触发InterceptorCallback
的onContinue方法,如果想拦截掉路由的处理,通过触发InterceptorCallback
的onInterrupt方法。如果所有的拦截都不拦截,则会回调到doInterceptions
方法的callback.onContinue(postcard);
这一句,而这一句最终回到了_Arouter._navigation方法,走正常的路由处理了。
Arouter的服务怎么设计的
上一节我们通过定义了HelloServiceImpl
的服务:
@Route(path = "/common/hello", name = "测试服务")
class HelloServiceImpl : HelloService {
override fun sayHello(name: String): String {
Log.d("HelloServiceImpl", "hello, $name")
return "hello, $name"
}
override fun init(context: Context) {}
}
然后我们可以通过传服务的path或class类都可以获取:
helloService2 =ARouter.getInstance().build("/common/hello").navigation() as HelloService
helloService3 = ARouter.getInstance().navigation(HelloService::class.java)
这也就是我们上面说的Iprovider可以通过两种形式获取,他们分别定义在了ARouter$$Group$$组名
、ARouter$$Providers$$模块名
中。
Arouter的注解属性怎么获取的
我们在定义属性的时候通过Autowired注解赋值,比如我上一节在shareActivity中定义如下属性:
@Autowired(name = "username")
lateinit var username: String
@Autowired
lateinit var testBean: TestBean
@Autowired(name ="listBean" )
lateinit var listBean: List<TestBean>
会在build下生成如下代码:
public class ShareActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
ShareActivity substitute = (ShareActivity)target;
substitute.username = substitute.getIntent().getExtras() == null ? substitute.username : substitute.getIntent().getExtras().getString("username", substitute.username);
if (null != serializationService) {
substitute.testBean = serializationService.parseObject(substitute.getIntent().getStringExtra("testBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestBean>(){}.getType());
} else {
}
if (null != serializationService) {
substitute.listBean = serializationService.parseObject(substitute.getIntent().getStringExtra("listBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestBean>>(){}.getType());
} else {
}
}
}
可以看到在inject方法中,获取了基本类型和对象类型,如果是基本类型,直接给属性赋值,如果是对象类型,先获取SerializationService
对应的实例,所以我们想定义对象类型的属性,需要实例化SerializationService
类型,那什么时候调用的inject方法,在定义属性的类中有这么一句:
ARouter.getInstance().inject(this)
最终到_Arouter.inject方法:
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
这里拿的AutowiredService
是AutowiredServiceImpl
,这跟InterceptorServiceImpl
获取方式是一样的,这里就不介绍了,直接看AutowiredServiceImpl
的doInject
方法:
private void doInject(Object instance, Class<?> parent) {
Class<?> clazz = null == parent ? instance.getClass() : parent;
ISyringe syringe = getSyringe(clazz);
//上面就拿到了ShareActivity$$ARouter$$Autowired类,调用inject方法
if (null != syringe) {
syringe.inject(instance);
}
Class<?> superClazz = clazz.getSuperclass();
if (null != superClazz && !superClazz.getName().startsWith("android")) {
doInject(instance, superClazz);
}
}
最终在这里调用了ShareActivity$$ARouter$$Autowired
的inject方法。
Arouter自动加载路由表
自动加载路由表是通过arouter-register来实现的,主要通过在编译期给LogisticsCenter
的loadRouterMap
方法插入register方法调用的代码:
我们可以通过反编译工具查看下apk下面的该类:
反编译出来的代码确实在loadRouterMap
方法处插入了register方法调用的代码,并且把registerByPlugin
置为true,所以最终不会通过扫描dex文件来加载路由表类装载到map中。
那大家想想和普通的通过扫描dex文件加载class有什么区别呢,我们在上面普通加载dex文件可以看到在初始化Arouter sdk的时候是非常慢的,因为它得扫描dex文件,然后加载dex文件里面的所有class,然后过滤出arouter需要的class文件,这还不是算慢得,如果虚拟机不支持MultiDex还会更慢,它会通过压缩所有的dex文件,然后压缩成zip,然后通过DexFile.loadDex转话成dex文件的集合,我们知道在
DexFile.loadDex
过程中会把普通的dex文件抓话成odex,这个过程是很慢的,关于dex文件转成odex做了些啥大家可以查查,具体我也不是很清楚,哈哈哈。最后通过遍历dex文件,拿到里面的class文件,最后过滤拿到Arouter需要的class,在我们项目中亲测arouter普通初始化是花了4秒多,所以我们可以看到Arouter自动加载路由表的插件对启动优化还是有很大改善的,Arouter自动加载路由表的插件是使用的通过gradle插装技术在编译期插入代码来达到自动加载路由表信息。 关于字节码插装技术我也不是很懂,所以我也会去恶补相关知识去了。
源码总结
- 在初始化阶段把所有的root、interceptor、provider信息分别存储到groupsIndex、interceptorsIndex、providersIndex中
- 然后初始化interceptorServiceImpl实例,顺便将interceptor加入到interceptors中。
- 在加载路由的时候,先去routes中取,如果没取到则去groupsIndex中拿group信息,再拿对应的metaMeta信息,将属性封装到postCard中,最后通过判断metaType是那种类型,做相应类型的处理。
- navigation有两种形式获取Iprovider,通过传class或path,如果传的是class则去interceptorsIndex找对应的Iprovder,如果是path则去groupsIndex中找,找到后,最终会保存在providers中。
思考源码的设计
-
关于这块的思考大家有什么想说的呢,Arouter通过定义两级的path,一级是group、二级是path,如果没有定义两级的path直接抛异常,这块改成一级的path会有什么问题吗?
-
首先通过group找到对应组的class对象,里面包装的该组下面所有的routeMeta信息,下次如果遇到相同group的路由,则直接去找routeMeta信息,这么设计有什么好处?
-
Arouter通过分组包装路由表,有快速查询路由的效率,所以我们可以尽量让Iprovider和Activity、fragment可以放到不同饿组里面,方便我们能快速拿到对应的routeMeta信息,是不是这样呢?
如果你还有什么要问的,欢迎评论区留言,当做互相学习!!!