前言
源码的解读是痛苦的,这里我打算带着几个问题从各个方面去解读源码 36164aab1652529611d3e899b586b97f145c70f7
结构介绍
如下图是源码的结构图,源码相关的库有三个java库comiler,interfaces,plugin,一个android的lib router,简单介绍一下各个库的作用
- compiler:根据
javax.annotation.processing.Processor
提供处理注解的功能来处理interfaces定义的注解,解析生成相应的源码文件,引入需要在app的build.gradle dependencies 添加annotationProcessor project(path: ':compiler')
- interfaces:定义了五种注解,分别是RouterPager、RouterRegex、RouterUri、RouterProvider、RouterService、另外ServiceImpl用来存储Service的实现类
- plugin:自定义一个名为WMRouter 的gradle插件,将注解生成器生成的初始化类汇总到ServiceLoaderInit,运行时直接调用ServiceLoaderInit。引用的时候需要在项目根目录build.gradle dependencies添加
classpath "com.sankuai.waimai.router:plugin:$VERSION_NAME"
,然后在app里面的build.gradle引入该pluginapply plugin: 'WMRouter
-
router:核心库
源码结构
如何识别并处理注解
java注解是在5.0开始提供的支持,可以参考文章。开发者可以使用java提供的一些内置的注解,也可以自定义注解,自定义注解的处理需要继承javax.annotation.processing.AbstractProcessor
,BaseProcessor继承自该类,对其进行了一次封装。我们以对RouterUri注解识别为例来讲解
打开compiler库的UriAnnotationProcessor类,可以看到类被注解了AutoService,点击跳转到源码,发现是一个引入的第三方库com.google.auto.service:auto-service:1.0-rc2
,这个库很简单就三个类,主要的作用是注解 processor 类,并对其生成 META-INF 的配置信息 。关键的处理代码在process方法
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
if (annotations == null || annotations.isEmpty()) {
return false;
}
CodeBlock.Builder builder = CodeBlock.builder();
String hash = null;
for (Element element : env.getElementsAnnotatedWith(RouterUri.class)) {
if (!(element instanceof Symbol.ClassSymbol)) {
continue;
}
boolean isActivity = isActivity(element);
boolean isHandler = isHandler(element);
if (!isActivity && !isHandler) {
continue;
}
Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element;
RouterUri uri = cls.getAnnotation(RouterUri.class);
if (uri == null) {
continue;
}
if (hash == null) {
hash = hash(cls.className());
}
CodeBlock handler = buildHandler(isActivity, cls);
CodeBlock interceptors = buildInterceptors(getInterceptors(uri));
// scheme, host, path, handler, exported, interceptors
String[] pathList = uri.path();
for (String path : pathList) {
builder.addStatement("handler.register($S, $S, $S, $L, $L$L)",
uri.scheme(),
uri.host(),
path,
handler,
uri.exported(),
interceptors);
}
}
buildHandlerInitClass(builder.build(), "UriAnnotationInit" + Const.SPLITTER + hash,
Const.URI_ANNOTATION_HANDLER_CLASS, Const.URI_ANNOTATION_INIT_CLASS);
return true;
}
这里引入了另外一个库com.squareup:javapoet:1.7.0
,这个库的主要作用就是帮助我们通过类调用的形式来生成代码。
在process生成了被RouterUri注解的代码,最后调用buildHandlerInitClass方法写入到com.sankuai.waimai.router.generated目录下。
至此生成了代码,形如
public class UriAnnotationInit_72565413b8384a4bebb02d352762d60d implements IUriAnnotationInit {
public void init(UriAnnotationHandler handler) {
handler.register("", "", "/show_toast_handler", new ShowToastHandler(), false);
handler.register("", "", "/advanced_demo", "com.sankuai.waimai.router.demo.advanced.AdvancedDemoActivity", false);
handler.register("", "", "/nearby_shop_with_location", "com.sankuai.waimai.router.demo.advanced.location.NearbyShopActivity", false, new LocationInterceptor());
handler.register("", "", "/home_ab_test", new HomeABTestHandler(), false);
handler.register("", "", "/browser", new BrowserSchemeHandler(), false);
...
}
}
生成的目录如下
其中被UriService注解的生成在service目录下。RouterUri、RouterPage、RouterRegex生成规则相似
UriService注解生成的规则和其他的不同,生成的代码如下
public class ServiceInit_b57118238b4f9112ddd862e55789c834 {
public static void init() {
ServiceLoader.put(Context.class, "/application", DemoApplication.class, true);
ServiceLoader.put(ILocationService.class, "/singleton", FakeLocationService.class, true);
ServiceLoader.put(Func0.class, "/method/get_version_code", GetVersionCodeMethod.class, true);
...
}
}
接下来就是如何调用生成的注解类呢,这里也分成了两类
调用RouterService注解的类比较麻烦,需要首先用plugin库的自定义gradle插件循环读取com.sankuai.waimai.router.generated.service下面类文件在com.sankuai.waimai.router.generated下生成ServiceLoaderInit类,如下
WMRouterTransform.class
private void generateServiceInitClass(String directory, Set<String> classes) {
if (classes.isEmpty()) {
WMRouterLogger.info(GENERATE_INIT + "skipped, no service found");
return;
}
try {
WMRouterLogger.info(GENERATE_INIT + "start...");
long ms = System.currentTimeMillis();
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, writer) {
};
String className = Const.SERVICE_LOADER_INIT.replace('.', '/');
cv.visit(50, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
Const.INIT_METHOD, "()V", null, null);
mv.visitCode();
for (String clazz : classes) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazz.replace('.', '/'),
"init",
"()V",
false);
}
mv.visitMaxs(0, 0);
mv.visitInsn(Opcodes.RETURN);
mv.visitEnd();
cv.visitEnd();
File dest = new File(directory, className + SdkConstants.DOT_CLASS);
dest.getParentFile().mkdirs();
new FileOutputStream(dest).write(writer.toByteArray());
WMRouterLogger.info(GENERATE_INIT + "cost %s ms", System.currentTimeMillis() - ms);
} catch (IOException e) {
WMRouterLogger.fatal(e);
}
}
生成类文件代码如下
public class ServiceLoaderInit {
public static void init() {
ServiceInit_aea7f96d0419b507d9b0ef471913b2f5.init();
ServiceInit_f3649d9f5ff15a62b844e64ca8434259.init();
ServiceInit_eb71854fbd69455ef4e0aa026c2e9881.init();
ServiceInit_b57118238b4f9112ddd862e55789c834.init();
ServiceInit_e694d982fb5d7a3a8c6b7085829e74a6.init();
ServiceInit_ee5f6404731417fe1433da40fd3c9708.init();
ServiceInit_9482ef47a8cf887ff1dc4bf705d5fc0a.init();
ServiceInit_36ed390bf4b81a8381d45028b37cc645.init();
}
}
然后在ServiceLoader的初始化利用反射调用ServiceLoaderInit类,然后Router调用ServiceLoader的lazyInit方法,最后在Application调用Router.lazyInit();完成将注解信息识别并且读取。
ServiceLoader.class
private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
@Override
protected void doInit() {
try {
// 反射调用Init类,避免引用的类过多,导致main dex capacity exceeded问题
Class.forName(Const.SERVICE_LOADER_INIT)
.getMethod(Const.INIT_METHOD)
.invoke(null);
Debugger.i("[ServiceLoader] init class invoked");
} catch (Exception e) {
Debugger.fatal(e);
}
}
};
Router.class
public static void lazyInit() {
ServiceLoader.lazyInit();
getRootHandler().lazyInit();
}
DemoApplication.class
// 懒加载后台初始化(可选)
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Router.lazyInit();
return null;
}
}.execute();
另外一种,RouterUri、RouterPager、RouterRegex调用,我们以RouterUri举例
DefaultAnnotationLoader.class
public <T extends UriHandler> void load(T handler,
Class<? extends AnnotationInit<T>> initClass) {
List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass);
for (AnnotationInit<T> service : services) {
service.init(handler);
}
}
RouterComponents.class
public static <T extends UriHandler> void loadAnnotation(T handler, Class<? extends AnnotationInit<T>> initClass) {
sAnnotationLoader.load(handler, initClass);
}
UriAnnotationHandler.class
protected void initAnnotationConfig() {
RouterComponents.loadAnnotation(this, IUriAnnotationInit.class);
}
public void lazyInit() {
mInitHelper.lazyInit();
}
DefaultRootUriHandler.class
public void lazyInit() {
mPageAnnotationHandler.lazyInit();
mUriAnnotationHandler.lazyInit();
mRegexAnnotationHandler.lazyInit();
}
Router.class
public static void lazyInit() {
ServiceLoader.lazyInit();
getRootHandler().lazyInit();
}
UriHandler解读
与注解连接的UriHandler
先看一下UriHandler继承结构(通过快捷键 Control + H )
红线圈起来的对应四个配合注解实现代码的注入,具体相关的方法是register,这个在前面解释注解说过(这里以UriAnnotationHandler举例)。
UriAnnotationHandler.class
public void register(String scheme, String host, String path,
Object handler, boolean exported, UriInterceptor... interceptors) {
// 没配的scheme和host使用默认值
if (TextUtils.isEmpty(scheme)) {
scheme = mDefaultScheme;
}
if (TextUtils.isEmpty(host)) {
host = mDefaultHost;
}
String schemeHost = RouterUtils.schemeHost(scheme, host);
PathHandler pathHandler = mMap.get(schemeHost);
if (pathHandler == null) {
pathHandler = createPathHandler();
mMap.put(schemeHost, pathHandler);
}
pathHandler.register(path, handler, exported, interceptors);
}
然后Application 通过DefaultRootUriHandler初始化来加载注解类放入对应的集合里
DefaultRootUriHandler.class
public DefaultRootUriHandler(Context context,
@Nullable String defaultScheme, @Nullable String defaultHost) {
super(context);
mPageAnnotationHandler = createPageAnnotationHandler();
mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
mRegexAnnotationHandler = createRegexAnnotationHandler();
// 按优先级排序,数字越大越先执行
// 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发
addChildHandler(mPageAnnotationHandler, 300);
// 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的Handler
addChildHandler(mUriAnnotationHandler, 200);
// 处理RouterRegex注解定义的正则匹配
addChildHandler(mRegexAnnotationHandler, 100);
// 添加其他用户自定义Handler...
// 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
addChildHandler(new StartUriHandler(), -100);
// 全局OnCompleteListener,用于输出跳转失败提示信息
setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);
}
那么,发起一个请求,如何知道是由哪个UriHandler处理呢,在UriRequest 请求里有Uri字段,就是根据该字段来识别的,具体描述如下。
- UriAnnotationHandler
根据scheme+host找出PathHandler,PathHandler都是存放拥有共同scheme+host,path不同的UriHandler,然后通过Path来获取指定已经注入的UriHandler
UriAnnotationHandler.class
private final Map<String, PathHandler> mMap = new HashMap<>();
/**
* 通过scheme+host找对应的PathHandler,找到了才会处理
*/
private PathHandler getChild(@NonNull UriRequest request) {
return mMap.get(request.schemeHost());
}
PathHandler.class
private UriHandler getChild(@NonNull UriRequest request) {
String path = request.getUri().getPath();
if (TextUtils.isEmpty(path)) {
return null;
}
if (TextUtils.isEmpty(mPathPrefix)) {
return mMap.get(path);
}
if (path.startsWith(mPathPrefix)) {
return mMap.get(path.substring(mPathPrefix.length()));
}
return null;
}
- RegexAnnotationHandler
RegexAnnotationHandler和DefaultRootUriHandler一样都继承自ChainedHandler,同样调用register方法加入一个UriHandler集合,只不过这里利用代理模式将UriHandler封装成RegexWrapperHandler,主要复写shouldHandle方法。最久在handleInternal循环去读取正则表达式匹配的UriHandler
RegexWrapperHandler.class
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return mPattern.matcher(request.getUri().toString()).matches();
}
ChainedHandler.class
private final PriorityList<UriHandler> mHandlers = new PriorityList<>();
@Override
protected void handleInternal(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
next(mHandlers.iterator(), request, callback);
}
private void next(@NonNull final Iterator<UriHandler> iterator, @NonNull final UriRequest request,
@NonNull final UriCallback callback) {
if (iterator.hasNext()) {
UriHandler t = iterator.next();
t.handle(request, new UriCallback() {
@Override
public void onNext() {
next(iterator, request, callback);
}
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode);
}
});
} else {
callback.onNext();
}
}
- PageAnnotationHandler
由于被RouterPage注解的scheme+host是固定的,所以相对于RouterUri少了一步根据scheme+host获取PathHandler步骤,原理和UriAnnotationHandler相似,这里不在讲解
解析成Intent完成跳转的UriHandler
解析成Intent
相关的类在activity包里
WX20181019-134849@2x.png
关键代码
AbsActivityHandler.class
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
// 创建Intent
Intent intent = createIntent(request);
if (intent == null || intent.getComponent() == null) {
Debugger.fatal("AbsActivityHandler.createIntent()应返回的带有ClassName的显式跳转Intent");
callback.onComplete(UriResult.CODE_ERROR);
return;
}
intent.setData(request.getUri());
UriSourceTools.setIntentSource(intent, request);
// 启动Activity
request.putFieldIfAbsent(ActivityLauncher.FIELD_LIMIT_PACKAGE, limitPackage());
int resultCode = RouterComponents.startActivity(request, intent);
// 回调方法
onActivityStartComplete(request, resultCode);
// 完成
callback.onComplete(resultCode);
}
PathHandler.class
public void register(String path, Object target, boolean exported,
UriInterceptor... interceptors) {
if (!TextUtils.isEmpty(path)) {
path = RouterUtils.appendSlash(path);
UriHandler parse = UriTargetTools.parse(target, exported, interceptors);
UriHandler prev = mMap.put(path, parse);
if (prev != null) {
Debugger.fatal("[%s] 重复注册path='%s'的UriHandler: %s, %s", this, path, prev, parse);
}
}
}
RegexAnnotationHandler.class
public void register(String regex, Object target, boolean exported, int priority,
UriInterceptor... interceptors) {
Pattern pattern = compile(regex);
if (pattern != null) {
UriHandler innerHandler = UriTargetTools.parse(target, exported, interceptors);
if (innerHandler != null) {
RegexWrapperHandler handler = new RegexWrapperHandler(pattern, priority,
innerHandler);
addChildHandler(handler, priority);
}
}
}
这里构造Intent完成Activity的跳转,具体的构造方式支持两种,一种是Class,一种是ClassName。实现分别对应的是ActivityHandler和ActivityClassNameHandler
被调用的地方如下
UriTargetTools.class
private static UriHandler toHandler(Object target) {
if (target instanceof UriHandler) {
return (UriHandler) target;
} else if (target instanceof String) {
return new ActivityClassNameHandler((String) target);
} else if (target instanceof Class && isValidActivityClass((Class) target)) {
//noinspection unchecked
return new ActivityHandler((Class<? extends Activity>) target);
} else {
return null;
}
}
public static UriHandler parse(Object target, boolean exported,
UriInterceptor... interceptors) {
UriHandler handler = toHandler(target);
if (handler != null) {
if (!exported) {
handler.addInterceptor(NotExportedInterceptor.INSTANCE);
}
handler.addInterceptors(interceptors);
}
return handler;
}
有没有有种“回到最初的地点”的感觉(register之前讲解过)
activity跳转
上面AbsActivityHandler handleInternal有一段代码
int resultCode = RouterComponents.startActivity(request, intent);
跟踪源码最终跳转到DefaultActivityLauncher#startActivity
DefaultActivityLauncher.class
public int startActivity(@NonNull UriRequest request, @NonNull Intent intent) {
if (request == null || intent == null) {
return UriResult.CODE_ERROR;
}
Context context = request.getContext();
// Extra
Bundle extra = request.getField(Bundle.class, FIELD_INTENT_EXTRA);
if (extra != null) {
intent.putExtras(extra);
}
// Flags
Integer flags = request.getField(Integer.class, FIELD_START_ACTIVITY_FLAGS);
if (flags != null) {
intent.setFlags(flags);
}
// request code
Integer requestCode = request.getField(Integer.class, FIELD_REQUEST_CODE);
// 是否限制Intent的packageName,限制后只会启动当前App内的页面,不启动其他App的页面,bool型
boolean limitPackage = request.getBooleanField(FIELD_LIMIT_PACKAGE, false);
// 设置package,先尝试启动App内的页面
intent.setPackage(context.getPackageName());
int r = startIntent(request, intent, context, requestCode, true);
if (limitPackage || r == UriResult.CODE_SUCCESS) {
return r;
}
// App内启动失败,再尝试启动App外页面
intent.setPackage(null);
return startIntent(request, intent, context, requestCode, false);
}
具体其它的调用可以看DefaultActivityLauncher.class源码
至此源码解析就告一段落了,有关UriInterceptor如何调用 、调用时,如何保证初始化已经完成、根据接口如何构造出类的实例、如何判断不同来源的跳转、降级策略、Gradle打包做了哪些优化、如何配置混淆 都又在文档里讲解,这里就不再描述。
网友评论