前面的文章我们大致了解了
WMRouter
的实现原理,本文就找一个具体的实例来看一下对于一个Activity页面的导航是如何实现的。
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
中的一些信息,设置到intent中优先启动APP内的界面。即在intent中设置自己的package
APP内启动失败,如果允许启动APP外的界面,则清空package,再次尝试启动
启动时如果设置有动画参数,则添加上
overridePendingTransition
拦截器的使用
我们这里就简单的看一下官方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
打开之前调用。监听登录状态,登录成功后则跳转目标界面。源码原理前面其实已经看过了,这里就不仔细看了。
作者:susion哒哒
链接:https://www.jianshu.com/p/221908cd237d