ARouter.getInstance().build("/test/activity2").navigation();
引用ARouter demo项目中一段页面跳转代码。
ARouter.getInstance
获取ARouter单例对象ARouter.build("/test/activity2")
,"/test/activity2"
参数是在@Route
注解中配置path
路径。返回值是一个Postcard
对象。Postcard.navigation()
,完成一次路由跳转。就这么简短的一段代码就完成了一次路由操作,下面分析一下其中做了哪些事情。
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;
}
}
采用双重锁来实例化ARouter单例对象。
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
Postcard
,继承自RouteMeta
,扩展了一些路由信息。它贯穿整个路由过程,一次路由就有对应的一个Postcard
。path
是路由地址,与@Route
注解中的path对应。需要确保唯一性
。_ARouter.build
方法protected Postcard build(String path) {
...
//预留的一个前置处理path的服务类。可以实现该接口,对所有path做处理。
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
...
}
PathReplaceService
是框架中预留的一个Path地址处理服务,可以统一处理Path地址。获取服务流程在后面服务篇中详细讲解。extractGroup(path)
。根据path提取默认的group字段。从path
中提取默认group
逻辑。当在@Route
中设置了group
字段时,发起路由时需要填入设置的字段。
private String extractGroup(String path) {
...
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;
}
}
...
}
需要'/'开头,并且至少有两个'/'
。不然会抛出异常。调到生成Postcard的方法。
protected Postcard build(String path, String group) {
...
//对path做二次处理
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
...
}
Postcard
对象,后续的路由都跟这个对象有关。分析完build流程,接下来看看navigation方法做了哪些操作。在build流程中生成Postcard对象,直接看Postcard.navigation
方法。经过一连串的调用,可以看到最后调用了_ARouter.navigation
。很长的一段逻辑,拆分成几段来分析
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
IProvider
接口,可以做一些通用的服务)NavigationCallback
这个接口。监听整个路由过程。public interface NavigationCallback {
void onFound(Postcard postcard);
void onLost(Postcard postcard);
void onArrival(Postcard postcard);
void onInterrupt(Postcard postcard);
}
看方法名就可以很清晰的看出来回调的时机了。
OK,继续下面的代码。
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
PretreatmentService
类是框架中预留的一个服务类(需要自己实现),可以在路由前做一些操作,比如可以cancel
掉这次的路由跳转。触发时机比拦截器更早。//完成对Postcard的最终封装
try {
LogisticsCenter.completion(postcard);
}catch (NoRouteFoundException ex) {
...
if (null != callback) {
callback.onLost(postcard);
} else {
// 对没有实现NavigationCallback接口的路由操作,可以统一有一个降级操作
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
}
这段代码主要两个关键点:
LogisticsCenter.completion(postcard)
。在这个方法中完成了对Postcard的最终封装,对后面路由操作起到了最关重要的作用,下面会详细分析。DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class)
这个服务可以做到对项目中的所有没有监听的路由操作做降级操作
。比如说路由地址写错了,这在运营配置跳转地址的时候很容易发生,这个时候可以通过这个服务做一个容错处理,不会导致应用出现异常。if (!postcard.isGreenChannel()) {
// 拦截器在异步中执行,避免耗时操作导致ANR
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
...
}
}
);
} else {
return _navigation(context, postcard, requestCode, callback);
}
postcard.isGreenChannel()
判断这个路由操作是否走绿色通道,也就是不受拦截器的处理。默认的获取Fragment
实例和Provider
是不受拦截器拦截的。当然也可以自己设置走绿色通过。异步线程
中完成的。执行完之后的回调是在主线程处理的。InterceptorCallback
接口中 onContinue
是执行最开始的路由操作(也可能会有改动)。onInterrupt
中表示被拦截器拦截,cancel掉了最开始的路由操作。拦截器的详细处理后面章节会详细分析。LogisticsCenter.completion
方法是如何完成对Postcard的封装的。在分析这段逻辑前,先看一下 Warehouse
数据类的组成。
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
Warehouse类里面代码不多,我们重点分析一下groupsIndex
变量。其他变量都是同一个逻辑。
先找到groupsIndex
添加元素的地方。
//伪代码
LogisticsCenter.init{
...
Set<String> routerMap;
...
//通过遍历dex文件中com.alibaba.android.arouter.routes包名下的类,获取到对应的全类名
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
...
for (String className : routerMap) {
...
//在loadInto中对Warehouse.groupsIndex添加元素
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
}
IRouteRoot
是一个接口,来看一下具体的实现类有哪些(基于ARouter框架GitHub上的demo项目)IRouteRoot
接口的类。类名的最后一段字符串就是项目中每个模块的模块名。app就是application库所在的模块的模块名。四个类就对应项目中的四个模块。类生成的规则在后续章节会详细讲解。挑第一个ARouter$$Root$$app
类来看一下里面的代码。
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}
Warehouse.groupsIndex
变量。Warehouse.groupsIndex
变量中。里面的key-value该如何理解呢?
其实这里的key对应的就是注解@Route
中path字段所对应的group
值。value就是这个group
对应的类,是一个容器类
,模块内的group
必须是唯一的。从这里就是可以看出来Warehouse.groupsIndex
变量存储的是项目中所有模块的分组信息。
接下来看一下ARouter$$Group$$test
类里面做了什么处理。
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", null, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
这里把所有的标注了注解@Route
的类汇集到了一起。把信息都汇总到RouteMeta中,包括了类型,具体的类对象,路由路径,分组信息,传递的参数信息,优先级,以及跳转动画
。
可以看到所有的路由对象信息都汇总到了atlas这个入参中,那么这个入参是从哪里传入的呢?回到Warehouse类中,看变量名,先猜测一下atlas这个入参所对应的变量应该就是routes
了(下面分析来看,其实就是这个)
其他变量不难看出:
providers和providersIndex
肯定是项目中服务类信息的汇总。interceptorsIndex和interceptors
是项目中拦截器类信息的汇总。groupsIndex
和routes
还是略有不同的。不同的下文会详细讲到。所以Warehouse
类其实就是一个容器类,包含了所有的路由信息(路由跳转,服务类,拦截器)。可以通过Warehouse
拿到不同类型的路由信息。
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
从上文分析Warehouse
可以看出Warehouse.routes
中拿到path对应的路由跳转对象信息。
if (null == routeMeta) {
// 当前路由信息可能不存在或者未加载
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
...
// 加载路由信息到内存中
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);
// 重新完善Postcard信息
..
}
如果获取到的路由跳转对象缓存是null就从新解析一遍。
group
拿到groupMeta
对象。如果为空抛出异常groupMeta
对象。执行loadInto
方法。此时所有的路由跳转对象信息都存到了Warehouse.routes
中。Warehouse.groupsIndex
中groupMeta
对应的组对象。减少内存占用。// 实际类对象
postcard.setDestination(routeMeta.getDestination());
// 路由对象类型。可以在RouteType枚举类中看到所有类型
postcard.setType(routeMeta.getType());
//优先级
postcard.setPriority(routeMeta.getPriority());
//额外字段
postcard.setExtra(routeMeta.getExtra());
//通过uri来设置额外字段
Uri rawUri = postcard.getUri();
if (null != rawUri) {
...
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
routeMeta
中路由对象信息存入到Postcard
中。注释中列出了详细信息。switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
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;
}
case PROVIDER
中根据routeMeta中的Destination对象来创建IProvider(服务类,拦截器实例都在这被创建)
mContext
是在初始化中传入的Application对象。greenChannel
绿色通道case FRAGMENT
中也设置greenChannel
绿色通道最终路由执行的方法
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
...
// Navigation in main looper.
runInMainThread(
...
);
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
//设置Fragment参数
...
return instance;
}
catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
...
}
根据不同的postcard.getType
来做不同的路由操作。
case ACTIVITY
当前路由类型是activity时,会执行页面跳转操作。有几点需要注意的。
currentContext
不是activity类型时,会设置Flag Intent.FLAG_ACTIVITY_NEW_TASK)
case PROVIDER
返回provider实例对象。case BOARDCAST ;case CONTENT_PROVIDER ;case FRAGMENT
这个三种类型会调用无参构造器创建实例对象,并返回。
到此ARouter整个路由过程就分析完了。可以看到中间涉及到了很多服务
和拦截器
的调用,后续文章就来分析一下 对这些服务和拦截器会分析。
对于文中有疑惑的地方,或者有任何意见和建议的地方都可以评论留言,我会第一时间回复~与君共勉。
上一篇:ARouter源码解析(一)-- 初始化分析
下一篇:ARouter源码解析(三)-- Provider和Interceptor源码分析