WMRouter(2)-路由節點的動態生成
文章是作者學習 WMRouter
的原始碼的重點紀要。 WMRouter官方文件 : ofollow,noindex">https://mp.weixin.qq.com/s/pKRi5qpZmol7xFIfeBbK_A
上一節我們分析了 WMRouter
的基本組成結構,並且我們知道了 DefaultRootUriHandler
在初始化的時候,會把一些 UrlHandler
加入到其handler列表中,比如 UriAnnotationHandler
:
public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) { ... mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost); addChildHandler(mUriAnnotationHandler, 200); ... }
先來說一下 UriAnnotationHandler
在 WMRouter
的大致功能:
@RouterUri
註解標記的Page,都會生成一個
UriHandler
2.在 UriAnnotationHandler
初始化的時候,會把這些生成的 UriHandler
,新增其內部的集合中,作為一個路由節點
3.在使用 Router.startUri()
方法時,會從 UriAnnotationHandler
尋找是否有滿足條件的 UriHandler
其他的 DefaultRootUriHandler
的子 UrlHandler
工作原理與它大致相同。
下面我們來一步一步看一下這些功能是怎麼實現的,首先看一下這個類:
UriAnnotationHandler
UriAnnotationHandler的構成
這個類主要由兩個功能:
- 繼承自
UrlHandler
,內部包含許多PathHandler
,可以處理多個(Scheme+Host)的Uri。 - 載入
@RouterUri
生成的UrlHandler
,並新增到其對應的PathHandler
中。作為路由節點
先來看一下 PathHandler
PathHandler
這個類也繼承自 UriHandler
,內部含有一個 UriHandler
的map,都是用來處理指定某一類(固定path字首字首)的Uri的。
回顧一下一個uri的組成: Scheme + //: + Host + / + Path + (?) + QueryParams
其實瀏覽原始碼,我並沒有看到太多設定path prefix的使用,因此這裡簡單了理解為 PathHandler
就是 UriHandler
的集合,可以通過 register()
來註冊一個 UriHandler
, 這個 UriHandler
就會以 path
為key, 它為value,放入map中:
public void register(String path, Object target, boolean exported, UriInterceptor... interceptors) { if (!TextUtils.isEmpty(path)) { path = RouterUtils.appendSlash(path); // 新增 path與host的分割斜線 `/` UriHandler UriHandler = UriTargetTools.parse(target, exported, interceptors); mMap.put(path, UriHandler); } } //UriTargetTools public static UriHandler parse(Object target, boolean exported,UriInterceptor... interceptors) { UriHandler handler = toHandler(target); //新增攔截器 return handler; } 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)) { return new ActivityHandler((Class<? extends Activity>) target); } else { return null; } }
通過上面原始碼我們可以知道,在註冊一個 UriHandler
是我們可以直接傳遞一個頁面的全類名、 UriHandler
、Activity的class例項。
繼續看一下這個類的 handlerInternal()
,這個方法會在 UriHandler.handle()
方法中呼叫,具體如何呼叫可以看前一篇文章 :
@Override protected void handleInternal(@NonNull final UriRequest request, @NonNull final UriCallback callback) { UriHandler h = mMap.get(request.getUri().getPath()); if (h != null) { h.handle(request, new UriCallback() { @Override public void onNext() { handleByDefault(request, callback); //page note found } @Override public void onComplete(int resultCode) { callback.onComplete(resultCode); } }); } else { handleByDefault(request, callback); } }
即根據 UriRequest
的path獲取對應的UriHandler,處理這個uri, 這樣我們就大致理解了 PathHandler
, 其實就是使用Map來儲存了許多 <path, UriHandler>。
繼續看 UriAnnotationHandler
, 這個類也有一個map,其型別為Map<String, PathHandler>。 key是 Scheme://Host
。它也提供一個 register()
方法來新增 PathHandler
。它也提供一個
public void register(String scheme, String host, String path, Object handler, boolean exported, UriInterceptor... interceptors) { 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); }
很簡單,即把handler放到對應key(scheme+host)的 PathHandler
中,如果 PathHandler
不存在則建立。
handleInternal
方法也很簡單:
@Override protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) { PathHandler pathHandler = getChild(request); if (pathHandler != null) pathHandler.handle(request, callback); else callback.onNext(); }
路由(Url)的註冊
上面分析後,我們知道 UriAnnotationHandler
提供了 register
方法來向其中註冊 UrlHandler
。那麼 register
是在哪裡呼叫的呢?在探討這個之前,我們先來看一下如何在 WMRouter
中定義一個路由節點(即,如何給定一個Url,然後跳轉到我們想要跳轉的page)。
在 WMRouter
中我們可以通過註解來定義路由的Page:
@RouterUri(scheme = "test", host = "channel1", path = "test_a") public class TestAActivity extends BaseActivity
我們在程式碼中可以使用 Router.startUri(context, "test://channel1/test_a")
, 跳轉到我們定義的這個Activity。其實 Router.startUri()
的具體實現就是呼叫 RootUriHandler
的方法開始整個路由遍歷:
public static void startUri(Context context, String uri) { getRootHandler().startUri(new UriRequest(context, uri)); }
按照我們目前對 WMRouter
的理解,應該有一個 UrlHandler
可以處理這個 uri
。那麼這個 UrlHandler
是怎麼來的呢?為了尋找處理這個 uri
的 UrlHandler
是什麼時候新增進去的。其實 WMRouter
會在編譯時它編譯時解析 @RouterUri
註解,並生成一些程式碼:
public class UriAnnotationInit_72565413b8384a4bebb02d352762d60d implements IUriAnnotationInit { public void init(UriAnnotationHandler handler) { handler.register("test", "channel1", "/test_a", "com.xxx.TestAActivity", false); } }
即在編譯時, WMRouter
就把添加了 @RouterUri
註解的 Activity
與其對應的 uri
生成了註冊到 UrlAnnotationHandler
的程式碼。這些程式碼會在執行時 UrlAnnotationHandler
初始化時呼叫。這樣我們在呼叫 Router.startUri()
,自然就可以導航到目標介面。
這裡我們先不討論,生成的程式碼是如何註冊到執行時 RootUriHandler
的 UriAnnotationHandler
例項中的,我們先來看一下這個程式碼是如何生成的?
要了解這段程式碼如何生成我們需要先了解一下:
- 註冊處理器: https://blog.csdn.net/jeasonlzy/article/details/74273851
- javaopet: https://blog.csdn.net/qq_18242391/article/details/77018155
作者就是使用上面這兩個工具,生成了上面的註冊程式碼。因此,這兩個技術就不在細看,我們來看一下主要流程:
解析註解,生成UriHandler註冊程式碼
我們直接來看一下 UriAnnotationProcessor
的主要處理邏輯:
@AutoService(Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) public class UriAnnotationProcessor extends BaseProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { CodeBlock.Builder builder = CodeBlock.builder(); String hash = null; for (Element element : env.getElementsAnnotatedWith(RouterUri.class)) { Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element; RouterUri uri = cls.getAnnotation(RouterUri.class); 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; } }
大致邏輯是依次處理每一個 @RouterUri
註解 scheme, host, path, handler, exported, interceptors
, 並利用這些引數生成呼叫 register
方法的程式碼:
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); ..... } }
我們可以大致畫一下 @RouterUri
、 UriAnnotationHandler
、 UriAnnotationProcessor
之間的關係:

UriAnnotationHandler_@RouterUri_UriAnnotationProcess.png