前言
DRouter是滴滴乘客端自研的一套Android路由框架,基于平臺化解耦的思想,為組件間通信服務,該專案以功能全面、易用為原則,支持各種路由場景,在頁面路由、服務獲取和過濾、跨行程及跨應用、VirtualApk插件支持等方面都能提供多樣化的服務,目前已在滴滴乘客端、順風車、單車、國際化、滴滴定制車中控、滴滴車載屏等十多個滴滴的app內使用,得到各種場景的驗證,
接入方式與使用
接入
github上的專案地址:github.com/didi/DRoute…
在專案根目錄下的build.gradle下添加插件依賴:
buildscript {
dependencies {
classpath "io.github.didi:drouter-plugin-proxy:1.0.1"
}
}
在主module的build.gradle檔案中應用插件:
plugins {
id 'com.didi.drouter'
}
在主module的build.gradle檔案中的dependencies中添加依賴:
dependencies {
api "io.github.didi:drouter-api:2.1.0"
}
引入成功后,需要在Application中進行初始化:
DRouter.init(application);
使用方法
這里只做簡述,詳情參考官方檔案:github.com/didi/DRoute…
Activity、Fragment、View
靜態注冊
Activity、Fragment、View只支持靜態跳轉,在Activity注冊路由地址:
@Router(scheme = "didi", host = "router", path = "/test1")
class TestActivity1 : AppCompatActivity()
發起頁面跳轉
DRouter.build("didi://router/test1")
.putExtra("tag", this.javaClass.name)
.start(this, object : RouterCallback.ActivityCallback() {
// Activity回呼
override fun onActivityResult(resultCode: Int, data: Intent?) {
}
})
Handler
Handler的典型應用場景是:
- 端外、Push、h5呼叫native代碼
- 無需下沉介面的組件化通信,有資料回傳能力
- 實作增強版eventbus
Handler支持靜態和動態兩種注冊方式,
靜態注冊
@Router(scheme = "didi", host = "router", path = "/sendOrder",
thread = Thread.WORKER) //指定執行執行緒
public class SendOrderHandler implements IRouterHandler {
@Override
void handle(@NonNull Request request, @NonNull Result result);
// result可以添加資料,會回傳給呼叫方
// 如果需要攔截后面的所有結點可以執行,默認不攔截
request.getInterceptor().onInterrupt();
}
}
動態定義和注冊
在內部注冊一個監聽者,不會重新實體化,類似EventBus,比EventBus增加了資料回傳的能力 如果注冊時使用了lifecycleOwner,會自動解注冊
// 動態注冊
IRegister register =
DRouter.register(
RouterKey.build("/dynamic/handler").setLifecycleOwner(lifecycleOwner),
new IRouterHandler() {
@Override
public void handle(@NonNull Request request, @NonNull Result result) {
}
});
// 解注冊,如果注冊時使用了生命周期,則可省
register.unregister();
發起導航
DRouter.build("didi://router/sendOrder")
.setHoldTimeout(3000) // 設定超時時間內未回傳則自動回呼callback
.start(context, new RouterCallback() {
@Override
public void onResult(@NonNull Result result) {
// 只有目標被釋放后才會回呼,且不會阻塞該執行緒
}
});
Hold
正常情況下Activity被start后以及RouterHandler的handle方法執行完成后,RouterCallback中的Result會立刻回傳,如果希望RouterCallback中的Result可以等待目標某個觸發時機然后才回傳Result,同時不阻塞當前執行緒,可以考慮暫存Result
應用場景
- 啟動登錄頁,等待用戶真正登陸成功后回呼
- 用戶端外冷啟動App,需要等待MainActivity的onResume回呼或某些事情準備好后再回呼
- RouterHandler有耗時任務,希望等待執行完后再回傳給呼叫方,又不阻塞呼叫方
配置
Activity和RouterHandler增加hold引數,就啟用了HoldResult
異步Activity
@Router(scheme = "didi", host = "router", path = "/login"
hold = "true")
public class ActivityLogin extends Activity {
@Override
protected void onCreate() {
super.onCreate();
Request request = RouterHelper.getRequest(this);
Result result = RouterHelper.getResult(this);
// 根據業務,在某個時間點觸發這個釋放(必須有)
RouterHelper.release(this);
}
}
異步RouterHandler
@Router(scheme = "didi", host = "router", path = "/sendOrder",
hold = "true")
public class SendOrderHandler implements IRouterHandler {
@Override
void handle(@NonNull Request request, @NonNull Result result);
// 根據業務,在某個時間點觸發這個釋放(必須有)
RouterHelper.release(request);
}
}
發起導航
DRouter.build("didi://router/sendOrder")
.setHoldTimeout(3000) // 設定超時時間內未回傳則自動回呼callback
.start(context, new RouterCallback() {
@Override
public void onResult(@NonNull Result result) {
// 只有目標被釋放后才會回呼,且不會阻塞該執行緒
}
});
Interceptor
攔截器可以在打開指定頁面時進行一些行為,比如:
- 目標頁面需要某種權限,比如某些場景下需要判斷用戶是否已登錄
- 目標頁面需要先進行某項操作,比如打開定位功能
定義攔截器
全域攔截器是指任意請求都會執行
@Interceptor(name = "interceptor1", //可選,可以使用名字替代類耦合的方式來參考到
priority = 1, //可選,優先級,值越大優先級越高
global = true) //可選,是否是全域攔截器
public class LoginInterceptor implements IRouterInterceptor {
@Override
public void handle(@NonNull Request request) {
if (isGo?) {
request.getInterceptor().onContinue();
} else {
request.getInterceptor().onInterrupt();
}
}
}
應用到頁面
@Router(path = "/main", interceptor = LoginInterceptor.class)
public class MainActvity extends Activity {}
原始碼分析
初始化流程

上述為DRouter的初始化流程圖,上述為DRouter的初始化流程圖,DRouter的專案結構為:
- drouter-api:直接供業務層呼叫
- drouter-api-stub:定義了MetaLoader和它的實作類,在編譯期生成路由表代碼
- drouter-plugin:真正執行DRouter構建流程的插件,執行RouterTask去創建RouterLoader的真正實作并處理注解,加載路由表,
- drouter-plugin-proxy:是一個Gradle Task,主要任務是拉取最新版本的drouter-plugin.jar包
其中drouter-api是直接供業務層呼叫的module,在api module中,依賴了drouter-api-stub:
compileOnly project(':drouter-api-stub')
只在編譯時有效,不會參與打包 可以在自己的moudle中使用該方式依賴一些比如com.android.support,gson這些使用者常用的庫,避免沖突,
drouter-api-stub中,定義了MetaLoader和它的實作類:RouterLoader、ServiceLoader和InterceptorLoader,而這些子類的方法都是空實作,在編譯期,會通過javaassit API和Gradle Transform API來實作具體的方法,
專案中直接參考的是drouter-plugin-proxy,根目錄下的build.gradle中:
classpath 'io.github.didi:drouter-plugin-proxy:1.0.1'
drouter-plugin-proxy是一個Gradle Task,主要任務是拉取最新版本的drouter-plugin.jar包
drouter-plugin內部是真正執行DRouter構建流程的插件,執行RouterTask去創建RouterLoader的真正實作并處理注解,加載路由表,
所以DRouter的初始化,可以從編譯和運行兩個時期去分析,
編譯期
通過Transform在專案編譯程序中在class編譯為dex的程序中,能夠獲取到所有的class類,并進行處理,DRouter用到了groovy插件 + Transform API在編譯期自動生成了路由表,首先,在drouter-plugin-proxy module中,創建了一個RouterPlugin:
class RouterPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
ProxyUtil.confirm(project)
project.extensions.create('drouter', RouterSetting)
project.android.registerTransform(new TransformProxy(project))
}
}
RouterPlugin最后注冊了一個TransformProxy:
class TransformProxy extends Transform {
// ...
@Override
void transform(TransformInvocation invocation) throws TransformException,
InterruptedException, IOException {
// 插件版本
String pluginVersion = ProxyUtil.getPluginVersion(invocation)
if (pluginVersion != null) {
File pluginJar = new File(project.rootDir, ".gradle/drouter/drouter-plugin-${pluginVersion}.jar")
if (!pluginJar.exists()) { // 若不存在,嘗試下載
// ...
}
if (pluginJar.exists()) {
URLClassLoader newLoader = new URLClassLoader([pluginJar.toURI().toURL()] as URL[], getClass().classLoader)
Class<?> transformClass = newLoader.loadClass("com.didi.drouter.plugin.RouterTransform") //【1】
ClassLoader threadLoader = Thread.currentThread().getContextClassLoader()
Thread.currentThread().setContextClassLoader(newLoader)
Constructor constructor = transformClass.getConstructor(Project.class)
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")
}
} else {
ProxyUtil.Logger.e("Error: there is no drouter-plugin version")
}
copyFile(invocation)
}
}
TransformProxy在下載最新的drouter-plugin插件,并在【1】處,創建了一個com.didi.drouter.plugin.RouterTransform物件,并執行了transform(invocation) ,查看RouterTransform的代碼:
class RouterTransform extends Transform {
@Override
void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException {
long timeStart = System.currentTimeMillis()
SystemUtil.debug = project.drouter.debug
File cacheFile = new File(tmpDir, "cache")
boolean configChanged = SystemUtil.configChanged(project)
boolean useCache = !isWindow && invocation.incremental && project.drouter.cache && cacheFile.exists() && !configChanged
if (useCache) {
cachePathSet.addAll(cacheFile.readLines())
}
if (!invocation.incremental) {
invocation.outputProvider.deleteAll()
}
compilePath = new ConcurrentLinkedQueue<>(project.android.bootClasspath)
for (TransformInput transformInput : invocation.inputs) {
handleDirectory(invocation, transformInput)
handleJar(invocation, transformInput)
}
File dest = invocation.outputProvider.getContentLocation("DRouterTable", TransformManager.CONTENT_CLASS,
ImmutableSet.of(QualifiedContent.Scope.PROJECT), Format.DIRECTORY) // 【2】
(new RouterTask(project, compilePath, cachePathSet, useCache, dest, tmpDir, project.drouter, isWindow)).run() // 【3】
FileUtils.writeLines(cacheFile, cachePathSet)
Logger.v("Link: https://github.com/didi/DRouter")
Logger.v("DRouterTask done, time used: " + (System.currentTimeMillis() - timeStart) / 1000f + "s")
}
}
【2】處創建了一個DRouterTable目錄,供RouterTask使用,最終執行到RouterTask的run方法:
void run() {
StoreUtil.clear();
JarUtils.printVersion(project, compileClassPath);
pool = new ClassPool();
classClassify = new ClassClassify(pool, setting);
startExecute();
}
private void startExecute() {
try {
long timeStart = System.currentTimeMillis();
for (File file : compileClassPath) {
pool.appendClassPath(file.getAbsolutePath());
}
if (useCache) {
loadCachePaths(cachePathSet);
} else {
loadFullPaths(compileClassPath);
}
timeStart = System.currentTimeMillis();
classClassify.generatorRouter(routerDir); // 【4】
} catch (Exception e) {
JarUtils.check(e);
String message = e.getMessage();
if (message == null || !message.startsWith("Class:")) {
e.printStackTrace();
}
throw new GradleException("DRouter: Could not generate router table\n" + e.getMessage());
} finally {
executor.shutdown();
FileUtils.deleteQuietly(wTmpDir);
}
}
【4】處ClassClassify的generatorRouter方法:
public void generatorRouter(File routerDir) throws Exception {
for (int i = 0; i < classifies.size(); i++) {
AbsRouterCollect cf = classifies.get(i);
cf.generate(routerDir);
}
}
AbsRouterCollect有三個實作類:
- RouterCollect
- InterceptorCollect
- ServiceCollect
ClassClassify在初始化時就添加了這三個:
public class ClassClassify {
private List<AbsRouterCollect> classifies = new ArrayList<>();
public ClassClassify(ClassPool pool, RouterSetting setting) {
// 初始化時就加入了三種AbsRouterCollect
classifies.add(new RouterCollect(pool, setting));
classifies.add(new ServiceCollect(pool, setting));
classifies.add(new InterceptorCollect(pool, setting));
}
// ...
}
這里以RouterCollect為例,RouterCollect的generate方法實作為:
@Override
public void generate(File routerDir) throws Exception {
// 創建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 {
StringBuilder interceptorClass = null;
StringBuilder interceptorName = null;
String uriValue = "";
String schemeValue = "";
String hostValue = "";
String pathValue = "";
Annotation annotation = null;
String type;
int thread = 0;
int priority = 0;
boolean hold = false;
if (routerCc.hasAnnotation(Router.class)) {
uriValue = ((Router) routerCc.getAnnotation(Router.class)).uri();
schemeValue = ((Router) routerCc.getAnnotation(Router.class)).scheme();
hostValue = ((Router) routerCc.getAnnotation(Router.class)).host();
pathValue = ((Router) routerCc.getAnnotation(Router.class)).path();
thread = ((Router) routerCc.getAnnotation(Router.class)).thread();
priority = ((Router) routerCc.getAnnotation(Router.class)).priority();
hold = ((Router) routerCc.getAnnotation(Router.class)).hold();
annotation = getAnnotation(routerCc, Router.class);
if (checkSuper(routerCc, "android.app.Activity")) {
type = "com.didi.drouter.store.RouterMeta.ACTIVITY";
} else if (checkSuper(routerCc,
"android.support.v4.app.Fragment", "androidx.fragment.app.Fragment")) {
type = "com.didi.drouter.store.RouterMeta.FRAGMENT";
} else if (checkSuper(routerCc, "android.view.View")) {
type = "com.didi.drouter.store.RouterMeta.VIEW";
} else if (checkSuper(routerCc, "com.didi.drouter.router.IRouterHandler")) {
type = "com.didi.drouter.store.RouterMeta.HANDLER";
} else {
throw new Exception("@Router target class illegal, " +
"support only Activity/Fragment/View/IRouterHandler");
}
} else {
pathValue = "/" + routerCc.getName().replace(".", "/");
type = "com.didi.drouter.store.RouterMeta.ACTIVITY";
}
// ...
if (isAnyRegex) {
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());
generatorClass(routerDir, ctClass, builder.toString());
}
在專案主目錄(一般是app, 這里用的是官方demo)下的demo/build/intermediates/transforms/DRouter/debug/...中查看生成的class類:
public class RouterLoader extends MetaLoader {
@Override
public void load(Map var1) {
var1.put("@@$$/activity/dynamic", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/dynamic", "com.didi.demo.handler.DynamicActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/remote", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/remote", "com.didi.demo.remote.RemoteActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/result", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/result", "com.didi.demo.activity.ActivityResultActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/router_page_single", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/router_page_single", "com.didi.demo.fragment.RouterPageSingleActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/router_page_stack", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/router_page_stack", "com.didi.demo.fragment.RouterPageStackActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/router_page_viewpager", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/router_page_viewpager", "com.didi.demo.fragment.RouterPageViewPagerActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/activity/test3", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/test3", "com.didi.demo.activity.ActivityTest3", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, true));
var1.put("@@$$/activity/webview", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/webview", "com.didi.demo.web.WebActivity", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/fragment/second", RouterMeta.build(RouterMeta.FRAGMENT).assembleRouter("", "", "/fragment/second", FragmentSecond.class, new com_didi_demo_fragment_FragmentSecond(), (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/handler/test1", RouterMeta.build(RouterMeta.HANDLER).assembleRouter("", "", "/handler/test1", HandlerTest1.class, new com_didi_demo_handler_HandlerTest1(), (Class[])null, (String[])null, 0, 1, false));
var1.put("@@$$/handler/test3", RouterMeta.build(RouterMeta.HANDLER).assembleRouter("", "", "/handler/test3", HandlerTest3.class, new com_didi_demo_handler_HandlerTest3(), (Class[])null, (String[])null, 2, 0, true));
var1.put("@@$$/view/bottom", RouterMeta.build(RouterMeta.VIEW).assembleRouter("", "", "/view/bottom", BottomView.class, new com_didi_demo_view_BottomView(), (Class[])null, (String[])null, 0, 0, false));
var1.put("@@$$/view/headview", RouterMeta.build(RouterMeta.VIEW).assembleRouter("", "", "/view/headview", HeadView.class, new com_didi_demo_view_HeadView(), (Class[])null, (String[])null, 0, 0, false));
var1.put("didi@@router$$/handler/test2", RouterMeta.build(RouterMeta.HANDLER).assembleRouter("didi", "router", "/handler/test2", HandlerTest2.class, new com_didi_demo_handler_HandlerTest2(), (Class[])null, (String[])null, 0, 0, false));
this.put("@@$$/activity/Test1_<Arg1>_<Arg2>", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("", "", "/activity/Test1_<Arg1>_<Arg2>", "com.didi.demo.activity.ActivityTest1", (IRouterProxy)null, new Class[]{InnerInterceptor.class}, new String[]{"interceptor1", "interceptor2"}, 0, 0, false), var1);
this.put("@@$$/fragment/first/.*", RouterMeta.build(RouterMeta.FRAGMENT).assembleRouter("", "", "/fragment/first/.*", FragmentFirst.class, new com_didi_demo_fragment_FragmentFirst(), (Class[])null, (String[])null, 0, 0, false), var1);
this.put("@@$$/handler/.*", RouterMeta.build(RouterMeta.HANDLER).assembleRouter("", "", "/handler/.*", HandlerAll.class, new com_didi_demo_handler_HandlerAll(), (Class[])null, (String[])null, 0, 2, false), var1);
this.put("didi@@www\\.didi\\.com$$/activity/test2", RouterMeta.build(RouterMeta.ACTIVITY).assembleRouter("didi", "www\\.didi\\.com", "/activity/test2", "com.didi.demo.activity.ActivityTest2", (IRouterProxy)null, (Class[])null, (String[])null, 0, 0, false), var1);
}
public RouterLoader() {
}
}
編譯期對RouterLoader的load方法進行了修改,將帶有注解的路由資訊添加到了路由表中,
運行期
從DRouter的使用方法入手,我們首先看看DRouter在初始化的時候做了什么:
public static void init(Application application) {
SystemUtil.setApplication(application); // 將application儲存到一個靜態變數中
RouterStore.checkAndLoad(RouterStore.HOST, true); // 【5】RouterStore.HOST = "host"
}
查看【5】部分的實作:
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.NAME, "DRouter start load router table sync");
load(app);
} else {
new Thread("drouter-table-thread") {
@Override
public void run() {
Log.d(RouterLogger.NAME, "DRouter start load router table in drouter-table-thread");
load(app);
}
}.start();
}
}
}
}
引數app是app唯一ID,若加載記錄中已存在,則不會重新加載路由表;引數async用來控制是同步還是異步加載路由表,真正的路由方法在load(app)中:
// class RouterStore
private static void load(String app) {
boolean load;
if (HOST.equals(app)) { // HOST = ”host“
load = loadHostTable(); // 【6】
initialized = true;
latch.countDown();
} else {
// 【7】
load = loadPluginTable(app,
Pair.create("Router", routerMetas),
Pair.create("Interceptor", interceptorMetas),
Pair.create("Service", serviceMetas));
}
if (!load) {
RouterLogger.getCoreLogger().e(
"DRouterTable in app \"%s\" not found, " +
"please apply drouter plugin first.", app);
}
}
當傳入的引數app等于HOST時,加載路由表的邏輯是loadHostTable(),若不相等,則是【7】處的方法,逐個分析:
private static boolean loadHostTable() {
try {
new RouterLoader().load(routerMetas);
new InterceptorLoader().load(interceptorMetas);
new ServiceLoader().load(serviceMetas);
} catch (NoClassDefFoundError e) {
return false;
}
return true;
}
另一種加載路由表的邏輯:
private static boolean loadPluginTable(String packageName, Pair... targets) {
try {
for (Pair<String, Map<?, ?>> target : targets) {
// 通過反射構造MetaLoader
MetaLoader loader = (MetaLoader) ReflectUtil.getInstance(
Class.forName(String.format("com.didi.drouter.loader.%s.%sLoader",
packageName, target.first))
);
assert loader != null;
loader.load(target.second);
}
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
兩種方式最終都是呼叫了loader.load()方法,load方法在編譯期已經將所有帶有注解的內容自動生成到方法中了,整個DRouter初始化到此結束,
動態添加路由表
Handler支持動態添加路由表,動態加載和loadHostTable中用到的三個引數也有關系:
// key is uriKey,value is meta, with dynamic
// key is REGEX_ROUTER,value is map<uriKey, meta>
private static final Map<String, Object> routerMetas = new ConcurrentHashMap<>();
// key is interceptor impl or string name
private static Map<Object, RouterMeta> interceptorMetas = new ConcurrentHashMap<>();
// key is interface,value is set, with dynamic
private static final Map<Class<?>, Set<RouterMeta>> serviceMetas = new ConcurrentHashMap<>();
routerMetas的另一種添加在register方法內:
// 注冊動態handler,key是routerKey,value是handler實體
@NonNull
@MainThread
public synchronized static IRegister register(final RouterKey key, final IRouterHandler handler) {
//...
// 構建meta資料
RouterMeta meta = RouterMeta.build(RouterMeta.HANDLER).assembleRouter(
key.uri.getScheme(), key.uri.getHost(), key.uri.getPath(),
(Class<?>) null, null, key.interceptor, key.interceptorName,
key.thread, 0, key.hold);
meta.setHandler(key, handler);
// 若包含正則運算式或占位符
if (meta.isRegexUri()) {
Map<String, RouterMeta> regexMap = (Map<String, RouterMeta>) routerMetas.get(REGEX_ROUTER);
if (regexMap == null) {
regexMap = new ConcurrentHashMap<>();
routerMetas.put(REGEX_ROUTER, regexMap);
}
regexMap.put(TextUtils.getStandardRouterKey(key.uri), meta);
} else {
routerMetas.put(TextUtils.getStandardRouterKey(key.uri), meta);
}
// 系結具有生命周期的物件銷毀時,解除注冊
if (key.lifecycleOwner != null) {
key.lifecycleOwner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
unregister(key, handler);
}
});
}
return new RouterRegister(key, handler);
}
serviceMetas的添加資料方法也是register,區別是支持泛型:
// 注冊動態服務,key是serviceKey,value是服務實體
@NonNull
@MainThread
public synchronized static <T> IRegister register(final ServiceKey<T> key, final T service) {
// ...
RouterMeta meta = RouterMeta.build(RouterMeta.SERVICE).assembleService(
null, null,
key.alias, key.feature, 0, Extend.Cache.NO);
meta.setService(key, service);
key.meta = meta;
Set<RouterMeta> metas = serviceMetas.get(key.function);
if (metas == null) {
metas = Collections.newSetFromMap(new ConcurrentHashMap<RouterMeta, Boolean>());
serviceMetas.put(key.function, metas);
}
metas.add(meta);
if (key.lifecycleOwner != null) {
key.lifecycleOwner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
unregister(key, service);
}
});
}
return new RouterRegister(key, service);
}
interceptorMetas沒有動態添加邏輯,因為攔截器和Activity/Fragment/View一樣沒有動態注冊,
呼叫流程
呼叫方式為:
DRouter.build(scheme).start(context);
build方法:
/**
* Navigation to router
* there will be only one activity match at most, but may be several router handler.
* @param uri String
* @return request
*/
@NonNull
public static Request build(String uri) {
return Request.build(uri);
}
build方法回傳了一個Request物件,查看Request.build(uri);的實作邏輯:
public static Request build(String uri) {
return new Request(uri == null ? Uri.EMPTY : Uri.parse(uri));
}
Request的構造方法為:
private Request(@NonNull Uri uri) {
this.uri = uri;
this.serialNumber = String.valueOf(counter.getAndIncrement());
putExtra(REQUEST_BUILD_URI, uri.toString());
}
然后再看看Request的start方法:
public void start(Context context) {
start(context, null);
}
public void start(Context context, RouterCallback callback) {
this.context = context == null ? DRouter.getContext() : context;
RouterLoader.build(this, callback).start(); //【8】
}
真正邏輯在【8】處,先看RouterLoader的build方法:
@NonNull
static RouterLoader build(Request request, RouterCallback callback) {
RouterLoader loader = new RouterLoader();
loader.primaryRequest = request; // 保存request
loader.callback = callback; // 保存callback
return loader;
}
RouterLoader的build方法回傳了一個loader物件,看看它的start方法:
void start() {
if (!TextUtils.isEmpty(primaryRequest.authority)) {
startRemote();
} else {
startLocal();
}
}
根據request的authority屬性是否為空來判斷是遠程還是本地呼叫,authority的值是遠程行程的 ContentProvider 權限,可以在呼叫跳轉邏輯時進行設定:
DRouter.build("/handler/test1")
.putExtra("1", 1)
.putExtra("2", new Bundle())
.putAddition("3", new ParamObject())
.setRemoteAuthority("com.didi.drouter.remote.demo.host")
.start(DRouter.getContext());
兩個關鍵方法: startRemote 和 startLocal
startLocal
private void startLocal() {
TextUtils.appendExtra(primaryRequest.getExtra(), TextUtils.getQuery(primaryRequest.getUri()));
// 按順序,優先級優先,然后完全匹配優先
Map<Request, RouterMeta> requestMap = makeRequest(); // 【9】
if (requestMap.isEmpty()) {
RouterLogger.getCoreLogger().w("warning: there is no request target match");
new Result(primaryRequest, null, callback);
ResultAgent.release(primaryRequest, ResultAgent.STATE_NOT_FOUND);
return;
}
final Result result = new Result(primaryRequest, requestMap.keySet(), callback);
if (requestMap.size() > 1) {
RouterLogger.getCoreLogger().w("warning: request match %s targets", requestMap.size());
}
final boolean[] stopByRouterTarget = {false};
// 遍歷request
for (final Map.Entry<Request, RouterMeta> entry : requestMap.entrySet()) {
if (stopByRouterTarget[0]) {
// one by one
ResultAgent.release(entry.getKey(), ResultAgent.STATE_STOP_BY_ROUTER_TARGET);
continue;
}
// 攔截器優先處理
InterceptorHandler.handle(entry.getKey(), entry.getValue(), new IRouterInterceptor.IInterceptor() {
@Override
public void onContinue() {
entry.getKey().interceptor = new IRouterInterceptor.IInterceptor() {
@Override
public void onContinue() {
}
@Override
public void onInterrupt() {
RouterLogger.getCoreLogger().w(
"request \"%s\" stop all remains requests", entry.getKey().getNumber());
stopByRouterTarget[0] = true;
}
};
// 【10】it will release branch auto when no hold or no target
RouterDispatcher.start(entry.getKey(), entry.getValue(), result, callback);
entry.getKey().interceptor = null;
}
@Override
public void onInterrupt() {
ResultAgent.release(entry.getKey(), ResultAgent.STATE_STOP_BY_INTERCEPTOR);
}
});
}
}
【9】將request按照順序和優先級進行排序,
private Map<Request, RouterMeta> makeRequest() {
Map<Request, RouterMeta> requestMap = new LinkedHashMap<>();
// 從intent中獲取序列化資料
Parcelable parcelable = primaryRequest.getParcelable(Extend.START_ACTIVITY_VIA_INTENT);
if (parcelable instanceof Intent) { // intent
primaryRequest.getExtra().remove(Extend.START_ACTIVITY_VIA_INTENT); // 洗掉額外資訊
Intent intent = (Intent) parcelable;
PackageManager pm = primaryRequest.getContext().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); // 查找匹配的activities
if (!activities.isEmpty()) { // 存在匹配的activity
primaryRequest.routerType = RouterType.ACTIVITY; // request的type設定為ACTIVITY
requestMap.put(this.primaryRequest, RouterMeta.build(RouterType.ACTIVITY).assembleRouter(intent)); // 構建一個RouterMeta存入map
}
} else {
List<RouterMeta> metas = getAllRouterMetas(); // 獲取所有的路由表資訊
int index = 0;
for (RouterMeta routerMeta : metas) { // 開始遍歷
// 將占位符鍵和值注入到捆綁包中
if (!routerMeta.injectPlaceHolder(primaryRequest.getUri(), primaryRequest.extra)) {
continue;
}
// 構建request
Request request = createBranchRequest(
this.primaryRequest, metas.size() > 1, routerMeta.getRouterType(), index++);
requestMap.put(request, routerMeta);
}
}
return requestMap;
}
然后遍歷request集合,去執行request的攔截器,處理完后通過【10】RouterDispatcher進行分發:
class RouterDispatcher {
static void start(Request request, RouterMeta meta, Result result, RouterCallback callback) {
switch (meta.getRouterType()) {
case RouterType.ACTIVITY:
startActivity(request, meta, result, callback);
break;
case RouterType.FRAGMENT:
startFragment(request, meta, result);
break;
case RouterType.VIEW:
startView(request, meta, result);
break;
case RouterType.HANDLER:
startHandler(request, meta, result, callback);
break;
default:
break;
}
}
}
這里可以看出,只有Activity和handler支持callback,看下Activity:
private static void startActivity(Request request, RouterMeta meta, Result result, RouterCallback callback) {
// ...
boolean hasRequestCode = request.getExtra().containsKey(Extend.START_ACTIVITY_REQUEST_CODE);
int requestCode = hasRequestCode? request.getInt(Extend.START_ACTIVITY_REQUEST_CODE) : 1024;
if (context instanceof Activity && callback instanceof RouterCallback.ActivityCallback) { // 【11】
ActivityCompat2.startActivityForResult((Activity) context, intent,
requestCode, (RouterCallback.ActivityCallback) callback);
} else if (context instanceof Activity && hasRequestCode) { // 【12】
ActivityCompat.startActivityForResult((Activity) context, intent,
requestCode, intent.getBundleExtra(Extend.START_ACTIVITY_OPTIONS));
} else { // 【13】
ActivityCompat.startActivity(context, intent, intent.getBundleExtra(Extend.START_ACTIVITY_OPTIONS));
}
// 處理頁面影片
int[] anim = request.getIntArray(Extend.START_ACTIVITY_ANIMATION);
if (context instanceof Activity && anim != null && anim.length == 2) {
((Activity) context).overridePendingTransition(anim[0], anim[1]);
}
result.isActivityStarted = true;
// hold處理
if (meta.isHold() && callback != null) {
RouterLogger.getCoreLogger().w("request \"%s\" will be hold", request.getNumber());
Monitor.startMonitor(request, result); // 【14】
} else {
ResultAgent.release(request, ResultAgent.STATE_COMPLETE);
}
}
這里的【11】、【12】、【13】處,呼叫了不同的Activity啟動方法,當callback的型別為RouterCallback.ActivityCallback時,通過自定義的工具類ActivityCompat2.startActivityForResult啟動,而12、13則是呼叫系統的API直接startActivity:
static void startActivityForResult(@NonNull final Activity activity,
@NonNull final Intent intent, final int requestCode,
RouterCallback.ActivityCallback callback) {
final int cur = sCount.incrementAndGet();
sCallbackMap.put(cur, new Pair<>(new WeakReference<>(activity), callback));
final Active active;
if (activity instanceof FragmentActivity) { // 創建一個Fragment,系結到activity上
active = new HolderFragmentV4();
} else {
active = new HolderFragment();
}
active.getCompat().cur = cur;
active.attach(activity, intent, requestCode); // 【15】
}
這里如果Activity是FragmentActivity,創建HolderFragmentV4,否則HolderFragment,兩者都實作了Active介面,并呼叫了attach方法:
// public static class HolderFragmentV4 extends Fragment implements Active
@Override
public void attach(Activity activity, Intent intent, int requestCode) {
this.intent = intent;
this.requestCode = requestCode;
FragmentManager fragmentManager = ((FragmentActivity)activity).getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(this, TAG);
transaction.commit(); // add fragment
}
// public static class HolderFragment extends android.app.Fragment implements Active
@Override
public void attach(Activity activity, Intent intent, int requestCode) {
this.intent = intent;
this.requestCode = requestCode;
android.app.FragmentManager fragmentManager = activity.getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(this, TAG);
transaction.commit();
}
兩個fragment型別的生命周期,都會呼叫ActivityCompat2的對應方法:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCompat2.onCreate(savedInstanceState);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
activityCompat2.onActivityResult(getActivity(), resultCode, data);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
activityCompat2.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
super.onDestroy();
activityCompat2.onDestroy();
}
當生命周期呼叫到了onActivityResult時,也會呼叫activityCompat2的onActivityResult方法,真正對callback進行呼叫:
private void onActivityResult(Activity activity, int resultCode, Intent data) {
RouterCallback.ActivityCallback cb;
Pair<WeakReference<Activity>, RouterCallback.ActivityCallback> pair = sCallbackMap.get(cur);
if (pair != null && (cb = pair.second) != null) {
RouterLogger.getCoreLogger().d("HoldFragment ActivityResult callback success");
cb.onActivityResult(resultCode, data); // 【16】callback進行了呼叫
}
if (pair == null || pair.first == null || pair.first.get() != activity) {
// HoldFragment onActivityResult warn, for host activity changed, but still callback last host
}
sCallbackMap.remove(cur);
active.remove();
}
【16】處對callback進行了呼叫,
而【14】處的hold導致延遲執行callback,實際是通過一個handlerpostDelay訊息來延時處理的:
class Monitor {
private static Handler timeoutHandler;
static void startMonitor(final Request request, final Result result) {
long period = request.holdTimeout;
if (period > 0) {
check();
RouterLogger.getCoreLogger().d("monitor for request \"%s\" start, count down \"%sms\"",
request.getNumber(), period);
timeoutHandler.postDelayed(new Runnable() {
@Override
public void run() {
RouterExecutor.submit(new Runnable() {
@Override
public void run() {
ResultAgent.release(request, ResultAgent.STATE_TIMEOUT);
}
});
}
}, period);
}
}
private static void check() {
if (timeoutHandler == null) {
synchronized (Monitor.class) {
if (timeoutHandler == null) {
HandlerThread handlerThread = new HandlerThread("timeout-monitor-thread");
handlerThread.start();
timeoutHandler = new Handler(handlerThread.getLooper());
}
}
}
}
}
接下來是handler的實作:
private static void startHandler(final Request request, final RouterMeta meta,
final Result result, final RouterCallback callback) {
// dynamic
IRouterHandler handler = meta.getHandler();
if (handler == null) {
handler = meta.getRouterProxy() != null ? (IRouterHandler) meta.getRouterProxy().newInstance(null) : null;
}
if (handler != null) {
final IRouterHandler finalHandler = handler;
RouterExecutor.execute(meta.getThread(), new Runnable() {
@Override
public void run() {
if (meta.isHold()) {
RouterLogger.getCoreLogger().w("request \"%s\" will hold", request.getNumber());
}
finalHandler.handle(request, result);
if (meta.isHold() && callback != null) {
Monitor.startMonitor(request, result); // 【17】 postDelay 延時呼叫
} else {
ResultAgent.release(request, ResultAgent.STATE_COMPLETE); // 【18】直接呼叫onResult
}
}
});
} else {
ResultAgent.release(request, ResultAgent.STATE_ERROR);
}
}
RouterExecutor是一個執行器,內部有一個執行緒池物件可以用于指定執行的執行緒,在runnable中,handler呼叫了handle方法,callback則是通過是否hold來判斷是否立刻執行,
在【18】處,為什么會立即執行callback:
static void release(Request request, String reason) {
if (request != null) {
release(request.getNumber(), reason);
}
}
private synchronized static void release(String requestNumber, String reason) {
Result result = getResult(requestNumber);
if (result != null) {
if (result.agent.primaryRequest.getNumber().equals(requestNumber)) {
// all clear
for (String number : result.agent.branchRequestMap.keySet()) {
if (!result.agent.branchReasonMap.containsKey(number)) {
completeBranch(number, reason);
}
}
} else {
// branch only
completeBranch(requestNumber, reason);
}
// check and release primary
if (result.agent.branchReasonMap.size() == result.agent.branchRequestMap.size()) {
completePrimary(result);
}
}
}
這里的兩個方法:
completeBranch:
private synchronized static void completeBranch(String branchNumber, String reason) {
Result result = numberToResult.get(branchNumber);
if (result != null) {
if (STATE_TIMEOUT.equals(reason)) {
// 超時強制執行log
}
result.agent.branchReasonMap.put(branchNumber, reason);
numberToResult.remove(branchNumber);
}
}
completePrimary:
private synchronized static void completePrimary(@NonNull Result result) {
numberToResult.remove(result.agent.primaryRequest.getNumber());
if (result.agent.callback != null) {
result.agent.callback.onResult(result); // 【19】callback得到呼叫
}
if (!numberToResult.containsKey(result.agent.primaryRequest.getNumber())) {
RouterLogger.getCoreLogger().d("Request finish ");
}
}
最后再分析一下View和Fragment的啟動:
private static void startFragment(Request request, RouterMeta meta, Result result) {
result.routerClass = meta.getRouterClass();
if (request.getExtra().getBoolean(Extend.START_FRAGMENT_NEW_INSTANCE, true)) {
Object object = meta.getRouterProxy() != null ?
meta.getRouterProxy().newInstance(null) : null;
if (object instanceof Fragment) {
result.fragment = (Fragment) object;
result.fragment.setArguments(request.getExtra());
}
}
ResultAgent.release(request, ResultAgent.STATE_COMPLETE);
}
private static void startView(Request request, RouterMeta meta, Result result) {
result.routerClass = meta.getRouterClass();
if (request.getExtra().getBoolean(Extend.START_VIEW_NEW_INSTANCE, true)) {
Object object = meta.getRouterProxy() != null ?
meta.getRouterProxy().newInstance(request.getContext()) : null;
if (object instanceof View) {
result.view = (View) object;
result.view.setTag(request.getExtra());
}
}
ResultAgent.release(request, ResultAgent.STATE_COMPLETE);
}
View和Fragment的實作邏輯基本一致,創建一個物件保存到result中,
最后是遠程呼叫startRemote:
private void startRemote() {
Result result = new Result(primaryRequest, Collections.singleton(primaryRequest), callback);
RemoteBridge.load(primaryRequest.authority, primaryRequest.resendStrategy,
primaryRequest.lifecycleOwner != null ? new WeakReference<>(primaryRequest.lifecycleOwner) : null)
.start(primaryRequest, result, callback);
}
RemoteBridge的load方法保存了遠程呼叫的相關資料,start方法真正進行了呼叫:
public void start(final Request request, final Result result, RouterCallback callback) {
final RemoteCommand command = new RemoteCommand(RemoteCommand.REQUEST);
command.bridge = RemoteBridge.this;
command.resendStrategy = resendStrategy;
command.lifecycle = lifecycle;
command.uri = request.getUri().toString();
command.extra = request.getExtra();
command.addition = request.getAddition();
if (callback != null) {
command.binder = new IClientService.Stub() {
@Override
public RemoteResult callback(RemoteCommand resultCommand) {
result.extra = resultCommand.extra;
result.addition = resultCommand.addition;
// callback once, so release it
RouterHelper.release(request);
return null;
}
};
} else {
// no callback, so release immediately
RouterHelper.release(request);
}
execute(command);
}
底層原理是binder跨行程呼叫,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/377143.html
標籤:其他
