前言
我们在上一篇分析了ARouter注解解析器相关源码;ARouter原理解析之注解处理器,了解了ARouter通过APT技术在编译期动态生成代码以及其用途,这一篇我们着重分析下ARouter是如何实现activity之间路由跳转;
路由跳转源码分析
初始化工作
我们先找到ARouter的入口,也就是初始化的地方,对应ARouter.init
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
//hasInit用于保证代码只初始化一次
if (!hasInit) {
//logger就是一个工具类,咱不需要怎么关注
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
只是对外暴露的API类,_ARouter
是其真正的实现类,这样设计是采用了装饰模式,一方面是为了解耦,另一方面还包装了日志打印功能,同时也隐藏了内部实现,相较于private
修饰,这种方式灵活性更强;
接着我们进入实现类,跟下_ARouter.init(application)
这段代码;
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(mContext, executor)
这行,其中executor
是内部自定义的线程池;
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
...
Set<String> routerMap; //生成类的类名集合
//如果是debug版本或者是新版本,从apt生成的包中加载类
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//ClassUtils.getFileNameByPackageName 是根据包名查找对应包名下的类;
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
//如果查找的map集合不为空,则缓存至sp中
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缓存中读取类名
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//判断类型,使用反射实例化对象,并调用方法,遍历获取到的 class
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);
}
}
...
}
上面代码中,如果没有开启debug
模式,调试的过程中一般不会更改版本号,那么只会从之前的缓存中读取类信息,这样新添加的路由就不会加载到内存中,这点需要注意;
其中ROUTE_ROOT_PAKCAGE
是一个常量,是编译期间使用注解动态生成的目录;
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)
方法就是找到app的dex,然后遍历出属于com.alibaba.android.arouter.routes
包名下的所有类,打包成集合并返回;
可以想象遍历整个dex查找指定类名工作量何其之大,怎么优化呢?ARouter的arouter-gradle-plugin
模块中就使用ASM字节码插桩来解决这个非常消耗性能的问题;这一块不是本文的重点,这里一笔带过,有兴趣的小伙伴可以深入研究下。
上面代码可以简单总结下,初始化就是查找ROUTE_ROOT_PAKCAGE
包名下的类,获取实例并强化成IRouteRoot, IInterceptorGroup, IProviderGroup, 然后调用 loadInto 方法;
对应具体的apt生成的代码如下:
/ ARouter$$Root$$app.java
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
// 以分组做为 key, 缓存到 routes.
// routes 是指向 Warehouse.groupsIndex 的引用
routes.put("service", ARouter$$Group$$service.class);
routes.put("test", ARouter$$Group$$test.class);
}
}
// ARouter$$Group$$service.java
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$service implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
...
}
}
小结
自此初始化工作完成,总结一下ARouter会在编译期根据注解声明分析自动生成一些注入代码, 初始化工作主要是把这些注解的对象和对注解的配置缓存到 Warehouse 的静态对象中;
跳转源码分析
我们还是先从方法入口进行分析,我们通常使用ARouter.getInstance().build("/test/activity2").navigation();
这种方式进行activity跳转;我们跟进下ARouter类的build方法
;
//实际调用 _ARouter单例的build方法,返回一个Postcard对象!
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
继续跟进 _ARouter.getInstance().build(path)
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//根据path获取group,group模式是path中第一部分内容,例如 path = /test/activity1, group就是test,如果是手动申明group的,一定要手动传递,否则找不到
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
这里就是直接返回了Postcard
对象,里面保存了path
、group
信息,Postcard
是继承RouteMeta
类,我们直接看核心路由代码navigation()
方法的实现;
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
...
// 如果传入的context为空,则使用mContext(初始化时的applicationContext)
postcard.setContext(null == context ? mContext : context);
try {
//验证能否找到对应的postcard.path
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
...
if (null != callback) {
//如果没找到postcard的配置,就调用onLost回调方法,或者系统配置的"降级服务"回调
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;
}
...
}
我们来看下ogisticsCenter.completion(postcard)
是如何验证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!");
}
// 通过postcard 的 path 查找对应的 RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// 如果没找到, 可能是还没装载过, 需要根据 group 查找对应的 groups
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
// 如果没找到, 抛出 NoRouteFoundException 错误方法结束
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 {
//动态加载路由组,重新调用completion方法,此时对应的group已经缓存完成
addRouteGroupDynamic(postcard.getGroup(), null);
completion(postcard); // Reload
}
} else {
// 可以查找到 routeMeta, copy routeMeta 的原始数据到 postcard 中.
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
...
switch (routeMeta.getType()) {
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
// 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
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());
}
}
// 设置一个provider 引用
postcard.setProvider(instance);
// provider 默认设置跳过拦截器
postcard.greenChannel();
break;
case FRAGMENT:
// fragment 默认设置跳过拦截器
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
completion
方法主要是对 group 做一次懒式加载, 我们继续查看 navigation 方法下面的内容;
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
...
// 执行到这里就是找到了 postcard. 执行对应回调
if (null != callback) {
callback.onFound(postcard);
}
// 如果是"绿色通道", 则直接执行_navigation方法, 目前只有provider, fragment 默认是走绿色通道
if (!postcard.isGreenChannel()) {
// interceptorService 是 ARouter 配置的默认的拦截服务
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else {
// 绿色通道
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
interceptorService
是ARouter
配置的默认的拦截器,其实就是递归调用,当所有的拦截器执行完,重新回到_navigation
方法;
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
// 用线程池异步执行
LogisticsCenter.executor.execute(new Runnable() {
public void run() {
// 初始化一个同步计数类, 用拦截器的 size
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
// 执行拦截操作, 看到这猜是递归调用.直到 index 满足条件, 退出递归.
_excute(0, interceptorCounter, postcard);
// 线程等待计数完成, 等待300秒...
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);
}
}
});
}
我们继续跟进_navigation
方法
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
// 我们就先只分析 activity 的逻辑
switch (postcard.getType()) {
case ACTIVITY:
// 初始化 intent, 把参数也添加上
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// 在 UI 线程进行 start activity
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
if (requestCode > 0) { // Need start for result
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
// 动画设置
if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
});
break;
}
return null;
}
至此,activity路由跳转功能实现~~
总结
ARouter 初始化的时候会把注入的信息进行缓存到Warehouse
中,在进行 navigation
的时候, 根据缓存进行懒加载, 然后获取实际对象或者跳转activity;
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )