写在前面
这篇文章不讲ARouter的用法, 只是阅读源码, 体会下阿里大神的思想. 所以想了解ARouter怎么用的请绕道走开
一. 从navigation说起
ARouter进行界面跳转的时候就几乎上就一句代码
ARouter.getInstance().build(ARouterPath.ACTIVITY_PHOTOS)
.navigation(this)
这个代码调用后发生了什么, 一个一个顺着看
- build
在调用build之后, 其实就生成了一个PostCard对象, PostCard对象中从path解析出来group信息, 其他的什么都没有干
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
- navigation
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
...
try {
//补充postcard遗失的信息, 这一步比较重要, 下面单独讲
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
...
}
if (!postcard.isGreenChannel()) {
... //分主干流程, 忽略
} else {
//执行跳转
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
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
// 下面就从postCard拿到要跳转到目标, 进行跳转即可.
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;
...
}
从上面代码可以看出来, 主要完成了两部
- 通过LogisticsCenter.completion(postcard);来完善postcard的信息
- 通过_navigation方法进行跳转, 这个和系统进行Activity的跳转没有什么不同
那就看看LogisticsCenter.completion(postcard)做了什么
public synchronized static void completion(Postcard postcard) {
//从内存中读取已经加载的postcard数据
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
//先获取到IRouteGroup, 然后加载到内存
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
//如果内存中没有, 那就加载到内存中.
...
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//加载到内存
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
completion(postcard); // Reload
} else {
//从内宠着你甘家寨各种数据
...
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
}
}
如上面可以看到
- 会先从内存中找route信息
- 内存中找不到, 会先找对应的group信息, 也就是IRouteGroup
- 找到之后, 会从IRouteGroup中加载对应的route信息
- 所以ARouter官方介绍中讲到, 会分组加载,
二. 从ARouter.init说起
在Application onCreate的时候, 需要调用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中, 我们看下这个方法做了什么
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
...
//解析Router map信息, 这里的Router Map其实就是要找到的Class路径
Set<String> routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
//找到一个名字是com.alibaba.android.arouter.routes.ARouter$$Root$$app
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
...
}
从上面的代码中可以看到, 关键是找到com.alibaba.android.arouter.routes.ARouter$$Root$$app类, 我们在代码中找到他
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("account", ARouter$$Group$$account.class);
routes.put("app", ARouter$$Group$$app.class);
...
}
}
再看看他加载的class
public class ARouter$$Group$$account implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/account/bindPhone", RouteMeta.build(RouteType.ACTIVITY, BindPhoneActivity.class, "/account/bindphone", "account", null, -1, -2147483648));
}
}
可以看到, 调用这个类, 就可以获取到我们注解的Router Activity信息, 在第一章中, 跳转的逻辑就补全了
三. 编译的时候干了什么
从上面看出来, 代码的关键在于编辑的时候生成了com.alibaba.android.arouter.routes.ARouter$$Root$$app类和 com.alibaba.android.arouter.routes.ARouter$$Root$$app类, 这两个类肯定是在编译的阶段生成的.
我们继承ARouter框架的时候, 需要加上下面的配置
kapt 'com.alibaba:arouter-compiler:1.2.2'
这个就是用来生成类文件的, 具体怎么生成, 我们需要download github地址, 看源码
地址
https://github.com/alibaba/ARouter
主要的逻辑是RouteProcess中
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
...
//获取Route信息
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
this.parseRoutes(routeElements);
...
}
看一下parseRoutes的实现
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
...
//获取Activity的typeMirror
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
// Interface of ARouter
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
Build input param name.
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
// Activity or Fragment
if (types.isSubtype(tm, type_Activity)) {
// Get all fields annotation by @Autowired
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
}
routeMeta.setInjectConfig(injectConfig);
}
...
categories(routeMeta);
}
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// Generate groups
String groupFileName = NAME_OF_GROUP + groupName;
// 写入文件
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
四: 缺点
ARouter的优点不必说了.读完整个代码之后, 可以发现ARouter的一些缺点
- Fragment没有办法startActivityForResult
- 采用注解, 编译阶段生成代码, 且没有增量编译的部分, 导致编译速度减慢.
目前只看到这两个缺点, 其他的暂时没有发现.
五. 我学到了什么
下面这两小段总结放到新的博客里面吧.