WMRouter源码分析(4)-页面路由实例分析
前面的文章我们大致了解了
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
打开之前调用。监听登录状态,登录成功后则跳转目标界面。源码原理前面其实已经看过了,这里就不仔细看了。