之前我们学习了Arouter的使用,今天分析下源码实现原理。主要分3步
生成路由表
RouteProcessor负责生产路由表
ARouter框架使用编译时注解工具(Annotation Processing Tool 简称APT),用来编译时扫描和处理注解。
编译时,编译器会检查AbstractProcessor的子类,并调用AbstractProcessor的子类的process()方法,然后把添加了注解的元素传到process()方法中,在process()函数中生成新的java类文件。
RouterProcessor是一个注解处理器,是AbstractProcessor的子类,在它的process()方法中,会调用parseRouter()方法,parseRouter()调用JavaPoet API生成java代码,参数注解处理器和拦截器注解处理器同理。
具体代码分析:
com.alibaba.android.arouter.compiler.processor.java
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
this.parseRoutes(routeElements);//关键代码,得到所有带Route的类元素,进行解析处理
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void parseRoutes(Set extends Element> routeElements) throws IOException{
....
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
...
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
...
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
...
}
一顿操作猛如虎,找到所有带Route注解的页面,最终实际项目生产一些文件,同时生成路由表
在ARouter$$Group$$app中已经加载了全部的路由
package com.alibaba.android.arouter.routes;
import com.alibaba.android.arouter.facade.enums.RouteType;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.sun.module_main.MainActivity;
import com.sun.module_main.arouter.RouterDataActivity;
import com.sun.module_main.arouter.RouterFragment;
import com.sun.module_main.arouter.RouterLoginActivity;
import com.sun.module_main.arouter.RouterMainActivity;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$app implements IRouteGroup {
@Override
public void loadInto(Map atlas) {
atlas.put("/app/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/mainactivity", "app", null, -1, -2147483648));
atlas.put("/app/RouterDataActivity", RouteMeta.build(RouteType.ACTIVITY, RouterDataActivity.class, "/app/routerdataactivity", "app", new java.util.HashMap(){{put("BOOLEAN_KEY", 0); put("BOOLEAN_BEAN", 11); put("BOOLEAN_STRING", 8); put("BOOLEAN_INT", 3); }}, -1, -2147483648));
atlas.put("/app/RouterFragment", RouteMeta.build(RouteType.FRAGMENT, RouterFragment.class, "/app/routerfragment", "app", null, -1, -2147483648));
atlas.put("/app/RouterLoginActivity", RouteMeta.build(RouteType.ACTIVITY, RouterLoginActivity.class, "/app/routerloginactivity", "app", new java.util.HashMap(){{put("message", 8); }}, -1, -2147483648));
atlas.put("/app/RouterMainActivity", RouteMeta.build(RouteType.ACTIVITY, RouterMainActivity.class, "/app/routermainactivity", "app", null, -1, -2147483648));
}
}
路由以map("路径",activity.class)全给保存好了,路由表生成完毕.
加载路由表
使用中先加载路由表,具体流程如下
Arouter使用了门面设计模式,所有明面上的操作类是ARouter.java,实际逻辑类是_ARouter.java
使用ARouter必须先初始化
ARouter.java
/**
* Init, it must be call before used router.
*/
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.java
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);
LogisticsCenter类的注释为:
LogisticsCenter contains all of the map. //包含所有的集合
1,Creates instance when it is first used. //第一次使用时创建实例
2,Handler Multi-Module relationship map(*) //处理多模块组件间的对应关系
3,Complex logic to solve duplicate group definition//解决重复组定义的复杂逻辑
LogisticsCenter.java
/**
* LogisticsCenter init, load all metas in memory. Demand initialization 初始化 加载内存中全部的元素
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();//加载路由表
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");//插件主动加载
} else {
Set routerMap;
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
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() + "]");
}
}
遍历所有的路由集合,通过反射+loadInfo(上面生成的文件就有这个方法),将所有的路由放到仓库中Warehouse,这样就实现了路由的加载.
使用路由表
使用过程路由跳转时
1, ARouter.getInstance().build("/module_a/ARouterOneActivity")//看这一句关键代码
.withString("From", "hello,I'm from app module")
.navigation();
主要分析build("xx")
2, ARouter.java
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
3,_ARouter.java
/**
* Build postcard by path and default group
*/
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);
}
}
4, ARouter不干活,直接去_ARouter类中
/**
* Launch the navigation by type
*
* @param service interface of service
* @param return type
* @return instance of service
*/
public T navigation(Class extends T> service) {
return _ARouter.getInstance().navigation(service);//看这一句关键代码
}
5,_ARouter.java
protected T navigation(Class extends T> service) {
try {
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;
}
// Set application to postcard.
postcard.setContext(mContext);
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
6,去仓库里找路由,组装成postcard
LogisticsCenter.java
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());
}
}
这是执行ARouter.getInstance().build("xx/xx")执行的查找路由的过程,最终还是去路由表中查找
当执行到navigation()方法时,一路跟踪,最终到达_ARouter.java中的_navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback()方法
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
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 (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(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:
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;
}
根据postcard的类型,找到Activity,新建Intent,设置页面,最终在主线程完成页面跳转,至此,Arouter的整个流程分析完成。
SerializationService
之前有写到"传递对象时需做特殊序列化处理",为什么要重新实现SerializationService
package com.sun.commonlibrary.util;
import android.content.Context;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.service.SerializationService;
import com.google.gson.Gson;
import java.lang.reflect.Type;
@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
private Gson mGson;
@Override
public void init(Context context) {
mGson = new Gson();
}
@Override
public T json2Object(String text, Class clazz) {
checkJson();
return mGson.fromJson(text, clazz);
}
@Override
public String object2Json(Object instance) {
checkJson();
return mGson.toJson(instance);
}
@Override
public T parseObject(String input, Type clazz) {
checkJson();
return mGson.fromJson(input, clazz);
}
public void checkJson() {
if (mGson == null) {
mGson = new Gson();
}
}
}
其实道理很简单
public Postcard withObject(@Nullable String key, @Nullable Object value) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
mBundle.putString(key, serializationService.object2Json(value));
return this;
}
执行withObject()方法时,需要个SerializationService对象将对象转为json数据,所以必须实现个SerializationService(),同时用Router注解标识
至此,ARouter框架初步深入研究完成,感觉还是比较多懵懂的,后续再深入分析。
组件化学习代码位置:GitHub - xingzhesun215/Android_Componentized: 安卓组件化学习