前言
源碼的解讀是痛苦的,這裏我打算帶着幾個問題從各個方面去解讀源碼 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包裏
關鍵代碼
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打包做了哪些優化、如何配置混淆 都又在文檔裏講解,這裏就不再描述。