Skip to content

Latest commit

 

History

History
217 lines (164 loc) · 10.4 KB

路由节点的动态生成.md

File metadata and controls

217 lines (164 loc) · 10.4 KB

上一节我们分析了WMRouter的基本路由架构,并且我们知道了路由的起点是DefaultRootUriHandlerDefaultRootUriHandler含有许多子UriHandlerUriAnnotationHandler就是其中之一。 按照前面的分析,一个UriHandler会处理一个Uri。一般一个app的Uri(页面)都非常多,那么这么多的UriHandler是什么时候生成的? 是怎么放入路由体系中(路由表)的呢?本文我们就来看一下这么多UriHandler是如何生成的。

本文就来看一下UriAnnotationHandler中的子UriHandler是如何生成的。先来看一下UriAnnotationHandler :

UriAnnotationHandler

UriAnnotationHandler的构成

这个类主要有两个功能:

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

在弄清楚UriAnnotationHandler的构成之前,我们先来看一下什么是PathHandler

PathHandler

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

public class PathHandler extends UriHandler {
    private final CaseInsensitiveNonNullMap<UriHandler> mMap = new CaseInsensitiveNonNullMap<>(); //其实就是<String, UriHandler>类型的map
    ...
}

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

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

    //注册一个 UriHandler, key为路由的path
    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;
        }
    }

根据toHandler()可以知道在注册一个UriHandler是我们可以直接传递一个页面的全类名、UriHandler、Activity的class实例。UriTargetTools.parse()会解析,并生成对应的UriHandler实例。

继续看一下PathHandler这个类的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。

继续看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不存在则创建。

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

即根据UriRequest的 scheme+host获取对应的PathHandler,交由PathHandler处理,而PathHandler其实就是根据Uri的path,获取对应的UriHandler来处理这个uri

ok上面我们大致知道了UriAnnotationHandler的组成,以及对于一个UriRequest它是如何处理的。 那么它的构成基础UriHandler是怎么注册的呢?即如何添加到PathHandler中的呢?

路由节点的动态生成

上面分析后,我们知道UriAnnotationHandler提供了register方法来向其中注册uri。它会根据uri来生成对应的UriHandler。那么这些注册代码在哪里?怎么生成的呢?

我们先看一下如何在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()的具体实现就是调用DefaultRootUriHandler方法来开始整个路由节点的遍历:

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

按照我们目前对WMRouter的理解,应该有一个UrlHandler可以处理这个uri。那么这个UrlHandler是怎么来的呢?即是什么时候注册到DefaultRootUriHandler中的呢?。其实WMRouter会在编译时它编译时解析@RouterUri注解,并生成一些代码:

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

即在编译时,WMRouter生成了把@RouterUri注解的Activity与其对应的uri注册到UrlAnnotationHandler的代码。这些代码会在UrlAnnotationHandler初始化时调用。即调用了UrlAnnotationHandler.register()。 这样UrlAnnotationHandler中就会按照我们前面的分析,生成处理这个uriUriHandler。这样我们在调用Router.startUri(),自然就可以导航到目标界面。

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

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

使用上面这两个工具就可以生成上面的注册代码。因此,这两个技术就不在细看,不过你需要了解一下,不然接下来的内容不好理解:

动态扫描注解,生成UriHandler注册代码

我们直接来看一下处理@RouterUri的注解处理器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_xxx 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之间的关系:

ok,这一节我们知道了@RouterUri标注的页面会生成注册到UriAnnotationHandler中的代码。那这些代码在什么时候调用呢? 我们在下一篇文章再看 路由节点的加载

欢迎Star我的Android进阶计划,看更多干货。