#6月日新方案
前言
最近的一个新项目运用了Clean Architecture+模块化+MVVM架构,将主页每个tab对应的功用都放到独自的模块且不相互依赖,这时就有了模块间页面跳转的问题,经过一番研究挑选了滴滴的DRouter,由于其出色的功用、灵活的组件拆分,更重要的是生成路由表时支撑插件增量编译、多线程扫描,运行时异步加载路由表,支撑回调式ActivityResult,比ARouter好太多。本着用一个新结构,只会用还不行的原则,我决定去了解一下结构的原理,并给自己制定了以下几个问题:
1、结构的规划分层是什么样的?
2、它是怎么生成路由表的?
3、它是怎么加载路由表的?
4、比较于ARouter怎么进步了功用?
阅览官方文档
比较于直接一头扎进源码,先阅览官方的文档总是没错的,官方给了一篇介绍的文章,写得非常好,根本回答了我以上的一切问题。
滴滴开源DRouter:一款高效的Android路由结构
首先在介绍DRouter的亮点部分得到了问题2、3、4的答案。
路由表在编译期经过插件动态生成。插件会发动多线程同时异步处理一切的组件;增量扫描功用能够帮助开发者在第2次编译时,只对修改正的代码进行处理,极大地缩短路由表生成的时间。
在编译器运用gradle插件配合transform扫描一切的类,生成路由表,并且支撑增量扫描,回答了问题2。
另外结构初始化的时分发动子线程去加载路由表,不堵塞主线程的履行,尽其所能进步效率。
回答了问题3。
加载路由表、实例化路由、以及跨进程指令到达服务端后的分发这些惯例应该运用反射的场景,运用预占位或动态生成代码来替换成java的new创立和显式方式履行,最大限度的去防止反射履行,进步功用。
回答了问题4,经过削减运用反射提升了功用。
在原理和架构章节处给了一张架构的规划图:
整体架构分三层,自下而上是数据流层、组件层、开放接口层。
数据流层是DRouter最重要的中心模块,这里承载着插件生成的路由表、路由元素、动态注册、以及跨进程功用相关的序列化数据流。一切的路由流通都会从这里取得对应的数据,进而流向正确的方针。
RouterPlugin和MetaLoader负责生成路由表,路由元素指的是RouterMeta,寄存scheme/host/path等信息。
组件层,中心的路由分发、拦截器、生命周期、异步暂存和监控、ServiceLoader、多维过滤、Fragment路由,以及跨进程指令打包等。
开放接口层则是运用时接触到的一些类,API规划得也很简单易用,DRouter类和Request类别离只有75和121行代码。
问题1得到解答,到此处也对整个结构有了一个整体的知道。
阅览源码
1.初始化流程
调用DRouter.init(app)后的时序图如下:
默认是在子线程完成路由表加载,不影响主线程。
public static void checkAndLoad(final String app, boolean async) {
if (!loadRecord.contains(app)) {
// 两层校验锁
synchronized (RouterStore.class) {
if (!loadRecord.contains(app)) {
loadRecord.add(app);
if (!async) {
Log.d(RouterLogger.CORE_TAG, "DRouter start load router table sync");
load(app);
} else {
new Thread("drouter-table-thread") {
@Override
public void run() {
Log.d(RouterLogger.CORE_TAG, "DRouter start load router table in drouter-table-thread");
load(app);
}
}.start();
}
}
}
}
}
终究走到了RouterLoader的load办法来加载路由表到一个map中,仔细看它的引进途径是com.didi.drouter.loader.host.RouterLoader
,是不存在于源码中的,由于它是编译的时分生成的,方位位于app/build/intermediates/transforms/DRouter/dev/debug/../com/didi/drouter/loader/host/RouterLoader。
public class RouterLoader extends MetaLoader {
@Override
public void load(Map var1) {
var1.put("@@$$/browse/BrowseActivity", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/browse/BrowseActivity", "com.example.demo.browse.BrowseActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
}
public RouterLoader() {
}
}
public abstract class MetaLoader {
public abstract void load(Map<?, ?> data);
// for regex router
protected void put(String uri, RouterMeta meta, Map<String, Map<String, RouterMeta>> data) {
Map<String, RouterMeta> map = data.get(RouterStore.REGEX_ROUTER);
if (map == null) {
map = new ConcurrentHashMap<>();
data.put(RouterStore.REGEX_ROUTER, map);
}
map.put(uri, meta);
}
// for service
protected void put(Class<?> clz, RouterMeta meta, Map<Class<?>, Set<RouterMeta>> data) {
Set<RouterMeta> set = data.get(clz);
if (set == null) {
set = Collections.newSetFromMap(new ConcurrentHashMap<RouterMeta, Boolean>());
data.put(clz, set);
}
set.add(meta);
}
}
不难猜出其是在编译期加了一个transform,生成RouterLoader类时加入了load办法的具体完成,具体来说是javaassit API+Gradle Transform,所以去看看drouter-plugin在编译期做了什么。
2.编译期transform
直接看时序图。
创立了一个RouterPlugin,并且注册了一个Gradle Transform。
class RouterPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
...
project.android.registerTransform(new TransformProxy(project))
}
}
class TransformProxy extends Transform {
@Override
void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException {
String pluginVersion = ProxyUtil.getPluginVersion(invocation)
if (pluginVersion != null) {
...
if (pluginJar.exists()) {
URLClassLoader newLoader = new URLClassLoader([pluginJar.toURI().toURL()] as URL[], getClass().classLoader)
Class<?> transformClass = newLoader.loadClass("com.didi.drouter.plugin.RouterTransform")
ClassLoader threadLoader = Thread.currentThread().getContextClassLoader()
// 1.设置URLClassLoader
Thread.currentThread().setContextClassLoader(newLoader)
Constructor constructor = transformClass.getConstructor(Project.class)
// 2.反射创立一个RouterTransform
Transform transform = (Transform) constructor.newInstance(project)
transform.transform(invocation)
Thread.currentThread().setContextClassLoader(threadLoader)
return
} else {
ProxyUtil.Logger.e("Error: there is no drouter-plugin jar")
}
}
}
}
注释2处反射创立一个com.didi.drouter.plugin.RouterTransform目标,并履行其transform办法,此处真实处理transform逻辑,它的方位位于drouter-plugin模块。
class RouterTransform extends Transform {
@Override
void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException {
...
// 1.创立一个DRouterTable目录
File dest = invocation.outputProvider.getContentLocation("DRouterTable", TransformManager.CONTENT_CLASS,
ImmutableSet.of(QualifiedContent.Scope.PROJECT), Format.DIRECTORY)
// 2.履行RouterTask
(new RouterTask(project, compilePath, cachePathSet, useCache, dest, tmpDir, setting, isWindow)).run()
FileUtils.writeLines(cacheFile, cachePathSet)
Logger.v("Link: https://github.com/didi/DRouter")
Logger.v("DRouterTask done, time used: " + (System.currentTimeMillis() - timeStart) / 1000f + "s")
}
}
注释2处new了一个RouterTask目标,并履行其run办法,之后的log输出就是平常编译能看到的信息,表示transform的耗时。
public class RouterTask {
void run() {
StoreUtil.clear();
JarUtils.printVersion(project, compileClassPath);
pool = new ClassPool();
// 1.创立ClassClassify
classClassify = new ClassClassify(pool, setting);
startExecute();
}
private void startExecute() {
try {
...
// 2.履行ClassClassify的generatorRouter
classClassify.generatorRouter(routerDir);
Logger.d("generator router table used: " + (System.currentTimeMillis() - timeStart) + "ms");
Logger.v("scan class size: " + count.get() + " | router class size: " + cachePathSet.size());
} catch (Exception e) {
JarUtils.check(e);
throw new GradleException("Could not generate d_router table\n" + e.getMessage(), e);
} finally {
executor.shutdown();
FileUtils.deleteQuietly(wTmpDir);
}
}
}
重点在于ClassClassify这个类,其generatorRouter办法就是终究处理生成路由表的逻辑。
public class ClassClassify {
private List<AbsRouterCollect> classifies = new ArrayList<>();
public ClassClassify(ClassPool pool, RouterSetting.Parse setting) {
classifies.add(new RouterCollect(pool, setting));
classifies.add(new ServiceCollect(pool, setting));
classifies.add(new InterceptorCollect(pool, setting));
}
public void generatorRouter(File routerDir) throws Exception {
for (int i = 0; i < classifies.size(); i++) {
AbsRouterCollect cf = classifies.get(i);
cf.generate(routerDir);
}
}
}
结构函数处添加了RouterCollect
/ServiceCollect
/InterceptorCollect
,终究履行的是他们的generate办法,别离处理路由表、service、拦截器,咱们只看路由表的。
class RouterCollect extends AbsRouterCollect {
@Override
public void generate(File routerDir) throws Exception {
// 1.创立RouterLoader类
CtClass ctClass = pool.makeClass(getPackageName() + ".RouterLoader");
CtClass superClass = pool.get("com.didi.drouter.store.MetaLoader");
ctClass.setSuperclass(superClass);
StringBuilder builder = new StringBuilder();
builder.append("public void load(java.util.Map data) {\n");
for (CtClass routerCc : routerClass.values()) {
try {
// 处理注解、class类型等逻辑
...
StringBuilder metaBuilder = new StringBuilder();
metaBuilder.append("com.didi.drouter.store.RouterMeta.build(");
metaBuilder.append(type);
metaBuilder.append(").assembleRouter(");
metaBuilder.append("\"").append(schemeValue).append("\"");
metaBuilder.append(",");
metaBuilder.append("\"").append(hostValue).append("\"");
metaBuilder.append(",");
metaBuilder.append("\"").append(pathValue).append("\"");
metaBuilder.append(",");
if ("com.didi.drouter.store.RouterMeta.ACTIVITY".equals(type)) {
if (!setting.isUseActivityRouterClass()) {
metaBuilder.append("\"").append(routerCc.getName()).append("\"");
} else {
metaBuilder.append(routerCc.getName()).append(".class");
}
} else {
metaBuilder.append(routerCc.getName()).append(".class");
}
metaBuilder.append(", ");
...
metaBuilder.append(proxyCc != null ? "new " + proxyCc.getName() + "()" : "null");
metaBuilder.append(", ");
metaBuilder.append(interceptorClass != null ? interceptorClass.toString() : "null");
metaBuilder.append(", ");
metaBuilder.append(interceptorName != null ? interceptorName.toString() : "null");
metaBuilder.append(", ");
metaBuilder.append(thread);
metaBuilder.append(", ");
metaBuilder.append(priority);
metaBuilder.append(", ");
metaBuilder.append(hold);
metaBuilder.append(")");
...
if (isAnyRegex) {
// 2. 刺进路由表
items.add(" put(\"" + uri + "\", " + metaBuilder + ", data); \n");
//builder.append(" put(\"").append(uri).append("\", ").append(metaBuilder).append(", data); \n");
} else {
items.add(" data.put(\"" + uri + "\", " + metaBuilder + "); \n");
//builder.append(" data.put(\"").append(uri).append("\", ").append(metaBuilder).append("); \n");
}
} catch (Exception e) {
e.printStackTrace();
}
Collections.sort(items);
for (String item : items) {
builder.append(item);
}
builder.append("}");
Logger.d("\nclass RouterLoader" + "\n" + builder.toString());
// 3.生成代码
generatorClass(routerDir, ctClass, builder.toString());
}
}
}
此处逻辑比较多,但总体是清晰的,处理完注解和类型的判别,获取路由的信息,结构即将刺进的代码,最后统一在父类AbsRouterCollect的generatorClass处理load办法的生成,此时编译器的作业就完成了。
ARouter也供给了arouter-register插件,同是在编译期生成路由表,不同的是在生成代码时,ARouter运用的是ASM,DRouter运用Javassist,查了一下资料,ASM功用比Javassist更好,但更难上手,需要懂字节码知识,Javassist在复杂的字节码级操作上供给了更高等级的笼统层,因此完成起来更容易、更快,只需要懂很少的字节码知识,它运用反射机制。
3.运行期加载路由表
从头贴一下加载路由表的load办法。
public class RouterLoader extends MetaLoader {
@Override
public void load(Map var1) {
var1.put("@@$$/browse/BrowseActivity", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/browse/BrowseActivity", "com.example.demo.browse.BrowseActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
}
public RouterLoader() {
}
}
看下RouteMeta的build办法。
public static RouterMeta build(int routerType) {
return new RouterMeta(routerType);
}
可见是直接new的一个路由类,这与ARouter直接经过反射创立路由类不同,功用更好。
private static void register(String className) {
if (!TextUtils.isEmpty(className)) {
try {
// 1.反射创立路由类
Class<?> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
registerInterceptor((IInterceptorGroup) obj);
} else {
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}
} catch (Exception e) {
logger.error(TAG,"register class error:" + className, e);
}
}
}
4.总结
本文分析了DRouter路由部分的原理,其在编译器运用Gradle Transform和Javassist生成路由表,运行时new路由类,异步初始化加载路由表,完成了高功用。
5.参考
滴滴开源DRouter:一款高效的Android路由结构
DRouter的底层完成浅析
ARouter发动优化引发的探究