WMRouter(2)-路由节点的动态生成

作者: susion哒哒 | 来源:发表于2018-10-16 17:00 被阅读12次

    文章是作者学习WMRouter的源码的重点纪要。 WMRouter官方文档 : https://mp.weixin.qq.com/s/pKRi5qpZmol7xFIfeBbK_A

    上一节我们分析了WMRouter的基本组成结构,并且我们知道了DefaultRootUriHandler在初始化的时候,会把一些UrlHandler加入到其handler列表中,比如UriAnnotationHandler:

        public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) {
            ...
            mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
            addChildHandler(mUriAnnotationHandler, 200);
            ...
        }
    

    先来说一下UriAnnotationHandlerWMRouter的大致功能:
    1.所有@RouterUri注解标记的Page,都会生成一个UriHandler
    2.在UriAnnotationHandler初始化的时候,会把这些生成的UriHandler,添加其内部的集合中,作为一个路由节点
    3.在使用Router.startUri()方法时,会从UriAnnotationHandler寻找是否有满足条件的UriHandler

    其他的DefaultRootUriHandler的子UrlHandler工作原理与它大致相同。

    下面我们来一步一步看一下这些功能是怎么实现的,首先看一下这个类:

    UriAnnotationHandler

    UriAnnotationHandler的构成

    这个类主要由两个功能:

    1. 继承自UrlHandler,内部包含许多PathHandler,可以处理多个(Scheme+Host)的Uri。
    2. 加载@RouterUri生成的UrlHandler,并添加到其对应的PathHandler中。作为路由节点

    先来看一下PathHandler

    PathHandler

    这个类也继承自UriHandler,内部含有一个UriHandler的map,都是用来处理指定某一类(固定path前缀前缀)的Uri的。

    回顾一下一个uri的组成: Scheme + //: + Host + / + Path + (?) + QueryParams

    其实浏览源码,我并没有看到太多设置path prefix的使用,因此这里简单了理解为PathHandler就是UriHandler的集合,可以通过register()来注册一个UriHandler, 这个UriHandler就会以 path为key, 它为value,放入map中:

        public void register(String path, Object target, boolean exported, UriInterceptor... interceptors) {
            if (!TextUtils.isEmpty(path)) {
                path = RouterUtils.appendSlash(path); // 添加 path与host的分割斜线 `/`
                UriHandler UriHandler = UriTargetTools.parse(target, exported, interceptors);
                mMap.put(path, UriHandler);
            }
        }
    
        //UriTargetTools
        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)) {
                return new ActivityHandler((Class<? extends Activity>) target);
            } else {
                return null;
            }
        }
    

    通过上面源码我们可以知道,在注册一个UriHandler是我们可以直接传递一个页面的全类名、UriHandler、Activity的class实例。

    继续看一下这个类的handlerInternal(),这个方法会在UriHandler.handle()方法中调用,具体如何调用可以看前一篇文章 :

         @Override protected void handleInternal(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
            UriHandler h = mMap.get(request.getUri().getPath());
            if (h != null) {
                h.handle(request, new UriCallback() {
                    @Override public void onNext() {
                        handleByDefault(request, callback); //page note found
                    }
                    @Override public void onComplete(int resultCode) {
                        callback.onComplete(resultCode);
                    }
                });
            } else {
                handleByDefault(request, callback);
            }
        }
    

    即根据UriRequest的path获取对应的UriHandler,处理这个uri, 这样我们就大致理解了PathHandler, 其实就是使用Map来存储了许多 <path, UriHandler>。

    继续看UriAnnotationHandler, 这个类也有一个map,其类型为Map<String, PathHandler>。 key是Scheme://Host。它也提供一个register()方法来添加PathHandler。它也提供一个

        public void register(String scheme, String host, String path, Object handler, boolean exported, UriInterceptor... interceptors) {
            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);
        }
    

    很简单,即把handler放到对应key(scheme+host)的 PathHandler中,如果PathHandler不存在则创建。

    handleInternal方法也很简单:

        @Override protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
            PathHandler pathHandler = getChild(request);
            if (pathHandler != null) pathHandler.handle(request, callback); else callback.onNext();
        }
    

    路由(Url)的注册

    上面分析后,我们知道UriAnnotationHandler提供了register方法来向其中注册UrlHandler。那么register是在哪里调用的呢?在探讨这个之前,我们先来看一下如何在WMRouter中定义一个路由节点(即,如何给定一个Url,然后跳转到我们想要跳转的page)。

    WMRouter中我们可以通过注解来定义路由的Page:

    @RouterUri(scheme = "test", host = "channel1", path = "test_a")
    public class TestAActivity extends BaseActivity
    

    我们在代码中可以使用 Router.startUri(context, "test://channel1/test_a"), 跳转到我们定义的这个Activity。其实Router.startUri()的具体实现就是调用RootUriHandler的方法开始整个路由遍历:

        public static void startUri(Context context, String uri) {
            getRootHandler().startUri(new UriRequest(context, uri));
        }
    

    按照我们目前对WMRouter的理解,应该有一个UrlHandler可以处理这个uri。那么这个UrlHandler是怎么来的呢?为了寻找处理这个uriUrlHandler是什么时候添加进去的。其实WMRouter会在编译时它编译时解析@RouterUri注解,并生成一些代码:

    public class UriAnnotationInit_72565413b8384a4bebb02d352762d60d implements IUriAnnotationInit {
      public void init(UriAnnotationHandler handler) {
        handler.register("test", "channel1", "/test_a", "com.xxx.TestAActivity", false);
      }
    }
    

    即在编译时,WMRouter就把添加了@RouterUri注解的Activity与其对应的uri生成了注册到UrlAnnotationHandler的代码。这些代码会在运行时UrlAnnotationHandler初始化时调用。这样我们在调用Router.startUri(),自然就可以导航到目标界面。

    这里我们先不讨论,生成的代码是如何注册到运行时RootUriHandlerUriAnnotationHandler实例中的,我们先来看一下这个代码是如何生成的?

    要了解这段代码如何生成我们需要先了解一下:

    1. 注册处理器: https://blog.csdn.net/jeasonlzy/article/details/74273851
    2. javaopet: https://blog.csdn.net/qq_18242391/article/details/77018155

    作者就是使用上面这两个工具,生成了上面的注册代码。因此,这两个技术就不在细看,我们来看一下主要流程:

    解析注解,生成UriHandler注册代码

    我们直接来看一下UriAnnotationProcessor的主要处理逻辑:

    @AutoService(Processor.class)
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    public class UriAnnotationProcessor extends BaseProcessor {
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
            CodeBlock.Builder builder = CodeBlock.builder();
            String hash = null;
            for (Element element : env.getElementsAnnotatedWith(RouterUri.class)) {
                Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element;
                RouterUri uri = cls.getAnnotation(RouterUri.class);
                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;
        }
    }
    

    大致逻辑是依次处理每一个@RouterUri注解scheme, host, path, handler, exported, interceptors, 并利用这些参数生成调用register方法的代码:

    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);
        .....
      }
    } 
    

    我们可以大致画一下 @RouterUriUriAnnotationHandlerUriAnnotationProcessor之间的关系:

    UriAnnotationHandler_@RouterUri_UriAnnotationProcess.png

    相关文章

      网友评论

        本文标题:WMRouter(2)-路由节点的动态生成

        本文链接:https://www.haomeiwen.com/subject/inbhzftx.html