手记

WMRouter源码分析(4)-页面路由实例分析

前面的文章我们大致了解了WMRouter的实现原理,本文就找一个具体的实例来看一下对于一个Activity页面的导航是如何实现的。

Activity的路由

首先我们使用@RouterUri标记一个Activity可被路由:

@RouterUri(path = {DemoConstant.JUMP_ACTIVITY_1, DemoConstant.JUMP_ACTIVITY_2})public class TestBasicActivity extends BaseActivity {}

在指定路由界面时,我们可以不指定schemehost,直接指定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);

UriAnnotationHandlerregister方法,按照前面的分析,其实是调用PathHandlerregister:

    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)UriHandlerActivityClassNameHandler,我们来看一下这个类:

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)
在这个方法中主要做了一下事情:

  1. 取出UriRequest中的一些信息,设置到intent中

  2. 优先启动APP内的界面。即在intent中设置自己的package

  3. APP内启动失败,如果允许启动APP外的界面,则清空package,再次尝试启动

  4. 启动时如果设置有动画参数,则添加上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


0人推荐
随时随地看视频
慕课网APP