WMRouter(4)-Activity路由與攔截分析
文章是作者學習WMRouter
的原始碼的重點紀要。 WMRouter官方文件 :ofollow,noindex">https://mp.weixin.qq.com/s/pKRi5qpZmol7xFIfeBbK_A
前面的文章我們大致瞭解了WMRouter
的實現原理,本文就來看一下對於一個Activity頁面的導航是如何實現的。
首先我們使用@RouterUri
標記一個Activity
可被路由
@RouterUri(path = {DemoConstant.JUMP_ACTIVITY_1, DemoConstant.JUMP_ACTIVITY_2}) public class TestBasicActivity extends BaseActivity {}
在指定路由介面時,我們可以不指定scheme
、host
,直接指定path
即可。這樣指定後我們直接Router.startUri(DemoConstant.JUMP_ACTIVITY_1)
,就可以開啟這個介面,下面我們就根據我們前面對於原始碼的分析,來看一下這個具體是怎麼完成的:
首先,按照WMRouter
的設計,肯定會生成一個UriHandler
來處理這個uri,那是由哪個UriHandler
來處理呢?
按照我們前面的分析@RouterUri
標記的page,會被UriAnnotationProcessor
掃描,並動態註冊到UriAnnotationHandler
中:
handler.register("", "", "/jump_activity_1", "com.sankuai.waimai.router.demo.basic.TestBasicActivity", false); handler.register("", "", "/jump_activity_2", "com.sankuai.waimai.router.demo.basic.TestBasicActivity", false);
UriAnnotationHandler
的register
方法,按照前面的分析,其實是呼叫PathHandler
的register
:
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); } } 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)) { //noinspection unchecked return new ActivityHandler((Class<? extends Activity>) target); } else { return null; } }
因此我們可以確定處理Router.startUri(DemoConstant.JUMP_ACTIVITY_1)
的UriHandler
是ActivityClassNameHandler
,我們來看一下這個類:
ActivityClassNameHandler
我們來看一下它的定義:
public class ActivityClassNameHandler extends AbsActivityHandler { @NonNull private final String mClassName; public ActivityClassNameHandler(@NonNull String className) { mClassName = className; } @Override protected Intent createIntent(@NonNull UriRequest request) { return new Intent().setClassName(request.getContext(), mClassName); } }
這個類繼承自AbsActivityHandler
, 重寫了父類的createIntent()
, 就是返回了一個設定了類名的intent
。
public abstract class AbsActivityHandler extends UriHandler { ..... protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) { Intent intent = createIntent(request); if (intent == null || intent.getComponent() == null) { callback.onComplete(UriResult.CODE_ERROR); return; } intent.setData(request.getUri()); UriSourceTools.setIntentSource(intent, request); request.putFieldIfAbsent(ActivityLauncher.FIELD_LIMIT_PACKAGE, limitPackage());// 啟動Activity int resultCode = RouterComponents.startActivity(request, intent); callback.onComplete(resultCode); } //是否只啟動當前APP中的Activity protected boolean limitPackage() { return true; } @NonNull protected abstract Intent createIntent(@NonNull UriRequest request); ..... }
可以看到在建立好intent
後,真正的Activity
的啟動時委派給RouterComponents.startActivity(request, intent)
。其實最終啟動Activity
是呼叫DefaultActivityLauncher.startActivity(UriRequest, context)
在這個方法中主要做了一下事情:
UriRequest overridePendingTransition
分析
其實從上面也可以看到,你在使用@RouterUri
標記了多少個page,在程式執行時就會生成對應的UriHandler
,如果應用的頁面數量比較多,那麼這將會是對記憶體一個不小的開銷,因此對於這些UriHandler
的載入,我們可以採取一些措施,比如懶載入,來避免記憶體的壓力。
攔截器
我們這裡就簡單的看一下官方demo中對攔截器的使用 :
@RouterUri(path = DemoConstant.ACCOUNT_WITH_LOGIN_INTERCEPTOR, interceptors = LoginInterceptor.class) public class UserAccountActivity extends BaseActivity {} public class LoginInterceptor implements UriInterceptor { @Override public void intercept(@NonNull UriRequest request, @NonNull final UriCallback callback) { final IAccountService accountService = DemoServiceManager.getAccountService(); if (accountService.isLogin()) { callback.onNext(); } else { Toast.makeText(request.getContext(), "請先登入~", Toast.LENGTH_SHORT).show(); accountService.registerObserver(new IAccountService.Observer() { @Override public void onLoginSuccess() { accountService.unregisterObserver(this); callback.onNext(); } .... }); DemoServiceManager.getAccountService().startLogin(request.getContext()); } } }
LoginInterceptor
會在UserAccountActivity
開啟之前呼叫。監聽登入狀態,登入成功後則跳轉目標介面。原始碼原理前面其實已經看過了,這裡就不仔細看了。