内容简介:上周四美团外卖技术团队开源了一个 Android Router 的框架:美团官方博客介绍说框架有两大功能:URI 分发、ServiceLoader。看似是两个独立的功能,经过翻源码查看,在底层实现上 URI 分发的部分功能是依赖于 ServiceLoader 实现的,所以在接下来的几个代码解析中,都会先讲到 ServiceLoader,然后再说 URI 分发。ServiceLoader 注解有两个:RouterService、RouterProvider。
上周四美团外卖技术团队开源了一个 Android Router 的框架: WMRouter ,博客详细介绍了用法以及设计方案,还不熟悉的同学可以先去看一下。本篇博客将从代码的角度解析框架的设计与实现。
美团官方博客介绍说框架有两大功能:URI 分发、ServiceLoader。看似是两个独立的功能,经过翻源码查看,在底层实现上 URI 分发的部分功能是依赖于 ServiceLoader 实现的,所以在接下来的几个代码解析中,都会先讲到 ServiceLoader,然后再说 URI 分发。
注解器及 gradle 插件
ServiceLoader 注解
ServiceLoader 注解有两个:RouterService、RouterProvider。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface RouterService { ... } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RouterProvider { ... }
看注解声明中 @Retention,我们可以了解到:RouterService 为编译时注解,RouterProvider 为运行时注解。
我们先看 RouterService 在编译时会做什么操作呢?
// ServiceAnnotationProcessor 类中的部分方法 private HashMap<String, Entity> mEntityMap = new HashMap<>(); @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { if (env.processingOver()) { generateConfigFiles(); } else { processAnnotations(env); } return true; } // 获取使用 RouterService 的类以及参数值保存到 mEntityMap 中 private void processAnnotations(RoundEnvironment env) { for (Element element : env.getElementsAnnotatedWith(RouterService.class)) { ... Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element; RouterService service = cls.getAnnotation(RouterService.class); if (service == null) { continue; } List<? extends TypeMirror> typeMirrors = getInterface(service); String[] keys = service.key(); // 获取 key String implementationName = getBinaryName(cls); // 获取类名 boolean singleton = service.singleton(); // 是否为单例 for (TypeMirror mirror : typeMirrors) { String interfaceName = getClassName(mirror); Entity entity = mEntityMap.get(interfaceName); if (entity == null) { entity = new Entity(interfaceName); mEntityMap.put(interfaceName, entity); } if (keys.length > 0) { for (String key : keys) { if (key.contains(":")) { String msg = String.format("%s: 注解%s的key参数不可包含冒号", implementationName, RouterService.class.getName()); throw new RuntimeException(msg); } entity.put(key, implementationName, singleton); } } else { entity.put(null, implementationName, singleton); } } } } // 把 mEntityMap 中的值写入到 assets 文件中 private void generateConfigFiles() { for (Map.Entry<String, Entity> entry : mEntityMap.entrySet()) { String interfaceName = entry.getKey(); writeInterfaceServiceFile(interfaceName, entry.getValue().getContents()); } }
这里通过 RouterService 注解获取到对应的类和注解信息,保存到了 assets 文件夹中,最后随 apk 一起打包,小伙伴们可以解压 apk 看一下,目录 assets/wm-router.services/ 中就会有以接口名称命名的多个文件,每个文件中包含了他们的实例以及其他注解信息。这里也就是解释了这个框架为什么可以支持组件化。
Router 注解
Router 注解有是三种:RouterPage、RouterRegex、RouterUri。
这三种注解都是编译时注解,他们做的工作是大致相同的,我们就用 RouterPage 来举例看一下他对应的注解器:PageAnnotationProcessor
PageAnnotationProcessor: @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { if (annotations == null || annotations.isEmpty()) { return false; } CodeBlock.Builder builder = CodeBlock.builder(); String hash = null; for (Element element : env.getElementsAnnotatedWith(RouterPage.class)) { if (!(element instanceof Symbol.ClassSymbol)) { continue; } boolean isActivity = isActivity(element); // 判断使用注解的类是否为 Activity boolean isHandler = isHandler(element); // 判断使用注解的类是否为 UriHandler if (!isActivity && !isHandler) { continue; } Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element; RouterPage page = cls.getAnnotation(RouterPage.class); // 通过类获取注解 if (page == null) { continue; } if (hash == null) { hash = hash(cls.className()); } CodeBlock handler = buildHandler(isActivity, cls); CodeBlock interceptors = buildInterceptors(getInterceptors(page)); // path, handler, interceptors String[] pathList = page.path(); for (String path : pathList) { // 这里会自动生成 register urihandler 的代码,把每个注解的类注册进去 builder.addStatement("handler.register($S, $L$L)", path, handler, interceptors); } } writeHandlerInitClass(builder.build(), hash, Const.PAGE_CLASS, Const.PAGE_ANNOTATION_HANDLER_CLASS, Const.PAGE_ANNOTATION_INIT_CLASS); return true; }
之后运行到了 BaseProcessor 类的 writeHandlerInitClass 函数,来看一下做了什么:
public void writeHandlerInitClass(CodeBlock code, String hash, String genClassName, String handlerClassName, String interfaceName) { try { genClassName += Const.SPLITTER + hash; // 拼接类名 // 构造方法 MethodSpec methodSpec = MethodSpec.methodBuilder(Const.INIT_METHOD) .addModifiers(Modifier.PUBLIC) .returns(TypeName.VOID) .addParameter(TypeName.get(typeMirror(handlerClassName)), "handler") .addCode(code) .build(); // 类型 TypeSpec typeSpec = TypeSpec.classBuilder(genClassName) .addSuperinterface(TypeName.get(typeMirror(interfaceName))) .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .build(); // 写了一个 java 类 JavaFile.builder(Const.GEN_PKG, typeSpec) .build() .writeTo(filer); String fullImplName = Const.GEN_PKG + Const.DOT + genClassName; String config = new ServiceImpl(null, fullImplName, false).toConfig(); // 放 assets 中写配置信息 writeInterfaceServiceFile(interfaceName, Collections.singletonList(config)); } catch (IOException e) { e.printStackTrace(); } }
这里我们会发现在编译过程中,URI 注解器会帮我们自动生成一些类的代码(实现对应的 AnnotationInit 接口,并自动注册 urihandler),然后会把自己的实现类生成到 assets 文件中,就像我们之前提到的 RouterService 一样。
ServiceLoader 原理
router 模块的代码中有一个关键类:ServiceLoader,这个类中包含了类的获取以及实例的创建。
// 从 assets 中根据接口名加载对应的实现类,并保存到缓存 private void loadData() { InputStream is = null; BufferedReader reader = null; try { try { // 读取 assets 文件 is = Router.getRootHandler().getContext().getAssets().open(Const.ASSETS_PATH + mInterfaceName); } catch (FileNotFoundException e) { Debugger.w("assets file for interface '%s' not found", mInterfaceName); } if (is == null) { return; } reader = new BufferedReader(new InputStreamReader(is)); String ln; while ((ln = reader.readLine()) != null) { ServiceImpl impl = ServiceImpl.fromConfig(ln); // 根据保存的规则读取注解配置的信息 if (impl != null) { ServiceImpl prev = mMap.put(impl.getKey(), impl); String errorMsg = ServiceImpl.checkConflict(mInterfaceName, prev, impl); if (errorMsg != null) { Debugger.fatal(errorMsg); } } } } } // 通过类名和 IFactory 使用反射创建实例,并保存到缓存 private <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) { if (impl == null) { return null; } Class<T> clazz = ClassPool.get(impl); // 从 ClassPool 获取 class,看是否有缓存 if (impl.isSingleton()) { // 实现类是否声明为单例,如果是单例则需要在 SingletonPool 中查找实例 try { return SingletonPool.get(clazz, factory); } catch (Exception e) { Debugger.fatal(e); } } else { try { if (factory == null) { factory = RouterComponents.getDefaultFactory(); } T t = factory.create(clazz); // 创建实例 Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t); return t; } catch (Exception e) { Debugger.fatal(e); } } return null; }
总结一下 ServiceLoader 的原理及特性:
- 通过接口名称读取之前注解生成的配置文件,得到相关的实现类,并缓存到 ClassPool,这里可在使用时加载
- 可通过无参、context、自己实现 IFactory、注解 RouterProvider 反射创建实例
- 如果实现类注解为单例,则会保存到 SingletonPool 中
- Provider 也会缓存到 ProviderPool 中
Router 核心层
Router 核心层包含4个基础结构:UriHandler、UriRequest、UriInterceptor、UriCallback
UriRequest
public class UriRequest { private final Context mContext; private Uri mUri; private final HashMap<String, Object> mFields; private String mSchemeHost = null; ... }
UriRequest中包含Context、URI和Fields,其中Fields为HashMap<String, Object>,可以通过Key存放任意数据。简单起见,UriRequest类同时承担了Response的功能,跳转请求的结果,也会被保存到Fields中。 存放到Fields中的常见字段举例如下,也可以根据需要自定义,为了避免冲突,建议字段名用完整的包名开头。
- Intent的Extra参数,Bundle类型
- 用于startActivityForResult的RequestCode,int类型
- 用于overridePendingTransition方法的页面切换动画资源,int[]类型
- 本次跳转结果的监听器,OnCompleteListener类型
总结来说,UriRequest用于实现一次URI跳转中所有组件之间的通信功能。
UriInterceptor、UriCallback
public interface UriInterceptor { /** * 调用 {@link UriCallback#onNext()} 进行下一步,不拦截 * 调用 {@link UriCallback#onComplete(int)} 做拦截处理 */ void intercept(@NonNull UriRequest request, @NonNull UriCallback callback); } public interface UriCallback extends UriResult { /** * 处理完成,继续后续流程。 */ void onNext(); /** * 处理完成,终止分发流程。 * @param resultCode 结果,可参考 {@link UriResult} */ void onComplete(int resultCode); }
UriHandler
UriHandler用于处理URI跳转请求,可以嵌套从而逐层分发和处理请求。UriHandler是异步结构,接收到UriRequest后处理(例如跳转Activity等),如果处理完成,则调用callback.onComplete() 并传入ResultCode;如果没有处理,则调用 callback.onNext() 继续分发。
public abstract class UriHandler { protected ChainedInterceptor mInterceptor; /** * 处理URI。通常不需要覆写本方法。 * * @param request URI跳转请求 * @param callback 处理完成后的回调 */ public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) { if (shouldHandle(request)) { Debugger.i("%s: handle request %s", this, request); if (mInterceptor != null) { mInterceptor.intercept(request, new UriCallback() { @Override public void onNext() { handleInternal(request, callback); } @Override public void onComplete(int result) { callback.onComplete(result); } }); } else { handleInternal(request, callback); } } else { Debugger.i("%s: ignore request %s", this, request); callback.onNext(); } } /** * 是否要处理给定的URI。在 {@link UriInterceptor} 之前调用。 */ protected abstract boolean shouldHandle(@NonNull UriRequest request); /** * 处理URI。在 {@link UriInterceptor} 之后调用。 */ protected abstract void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback); }
UriHandler 设计的很好,感觉有些类似于 Android Touch 事件分发机制,通过 shouldHandle 判断该 UriHandler 是否要处理,如果返回 True,则会优先执行一次拦截器,之后在 handleInternal 中做出对应的处理。
mInterceptor 的类型为 ChainedInterceptor,其中包含一个 List 容器,保存了多个 nterceptor,这个下面会说到。
Chained 相关类
Chained 类:ChainedHandler、ChainedInterceptor,其中分别持有了多个 UriHandler、UriInterceptor。
- ChainedHandler 使用了自定义的 PriorityList (不过内部实现也是 LinkedList),里面做了一些对 UriHandler 优先级的判断,在 handleInternal 函数中对之前 排序 好的 mHandlers 执行 handle 方法
- UriInterceptor 使用 LinkedList,在 intercept 方法中,判断子拦截器是否需要拦截
public class ChainedHandler extends UriHandler { private final PriorityList<UriHandler> mHandlers = new PriorityList<>(); // 按优先级从大到小排列 @Override protected void handleInternal(@NonNull final UriRequest request, @NonNull final UriCallback callback) { next(mHandlers.iterator(), request, callback); } private void next(@NonNull final Iterator<UriHandler> iterator, @NonNull final UriRequest request, @NonNull final UriCallback callback) { if (iterator.hasNext()) { UriHandler t = iterator.next(); t.handle(request, new UriCallback() { @Override public void onNext() { next(iterator, request, callback); // 递归遍历所有的 riHandler } @Override public void onComplete(int resultCode) { callback.onComplete(resultCode); } }); } else { callback.onNext(); } } } // ChainedInterceptor 的实现和 ChainedHandler 基本类似,这里就不粘代码了
RootUriHandler
作为入口 UriHandler,继承 ChainedHandler,内部实现了 startUri(UriRequest) 和一个全局的监听
public class RootUriHandler extends ChainedHandler { private OnCompleteListener mGlobalOnCompleteListener; // 全局监听 public void startUri(@NonNull UriRequest request) { // 前面有一系列判空操作 ... handle(request, new RootUriCallback(request)); // 执行 handle,并传入自定义的 RootUriCallback } // RootUriCallback 实现对部分回调的重处理,以及全局回调的监听 protected class RootUriCallback implements UriCallback { @Override public void onComplete(int resultCode) { switch (resultCode) { case CODE_REDIRECT: // 重定向,重新跳转 Debugger.i("<--- redirect, result code = %s", resultCode); startUri(mRequest); break; case CODE_SUCCESS: // 跳转成功 mRequest.putField(UriRequest.FIELD_RESULT_CODE, resultCode); onSuccess(mRequest); Debugger.i("<--- success, result code = %s", resultCode); break; default: // 跳转失败 mRequest.putField(UriRequest.FIELD_RESULT_CODE, resultCode); onError(mRequest, resultCode); Debugger.i("<--- error, result code = %s", resultCode); break; } } } }
好,到此为止,从源码角度,我们会发现 URI 分发核心代码将会包含以下功能属性:
- 通过 UriRequest 发出请求,分发给 RootUriHandler,及其子 UriHandler
- ChainedHandler 可持有多个 UriHandler,用于处理URI跳转请求,可以嵌套从而逐层分发和处理请求,也同时具有优先级的功能
- UriHandler 持有 ChainedInterceptor,可对请求进行拦截处理,比如:添加请求参数、拦截处理、弹框、重定向等
- 大概执行过程为 UriRequest –> UriInterceptor –> RootUriHandler,也就是官方给出的设计思路图
Router 通用实现层
核心层接口默认实现
DefaultRootUriHandler 继承 RootUriHandler,对 RootUriHandler 做了进一步封装处理,并且默认包含了 PageAnnotationHandler、UriAnnotationHandler、RegexAnnotationHandler、StartUriHandler 四种 UriHandler 的支持
public class DefaultRootUriHandler extends RootUriHandler { public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) { ... // 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发 addChildHandler(mPageAnnotationHandler, 300); // 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的Handler addChildHandler(mUriAnnotationHandler, 200); // 处理RouterRegex注解定义的正则匹配 addChildHandler(mRegexAnnotationHandler, 100); // 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri addChildHandler(new StartUriHandler(), -100); ... } }
DefaultUriRequest 继承 UriRequest,增加了常用参数的辅助方法,方便使用。
DefaultAnnotationLoader 提供了加载加载注解配置的默认实现,通过 ServiceLoader 的方式。
public class DefaultAnnotationLoader implements AnnotationLoader { public static final AnnotationLoader INSTANCE = new DefaultAnnotationLoader(); @Override public <T extends UriHandler> void load(T handler, Class<? extends AnnotationInit<T>> initClass) { List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass); for (AnnotationInit<T> service : services) { service.init(handler); } } }
四种 UriHandler
- PageAnnotationHandler 处理所有 wm_router://page/* 形式的URI跳转,根据path匹配由 RouterPage 注解配置的节点
- UriAnnotationHandler 根据URI的 scheme + host,分发到对应的 PathHandler(如果有),之后 PathHandler 再根据path匹配 RouterUri 注解配置的节点
- RegexAnnotationHandler 根据优先级和正则匹配尝试将URI分发给 RouterRegex 配置的每个节点
- StartUriHandler 尝试直接使用 Android 原生的隐式跳转启动URI,用于处理其他类型的 URI,例如 tel: 、 mailto:
activity 跳转
- ActivityClassNameHandler 通过 ClassName 生成 Intent 交给 DefaultActivityLauncher 执行具体跳转任务
- ActivityHandler 通过 Activity 生成 Intent 交给 DefaultActivityLauncher 执行具体跳转任务
- DefaultActivityLauncher 为 startActivity 的默认实现,其中判断了通过 Action 跳转还是使用普通的方式跳转
下面贴部分代码:public class ActivityClassNameHandler extends AbsActivityHandler { ... protected Intent createIntent(@NonNull UriRequest request) { // 通过 ClassName 生成 Intent return new Intent().setClassName(request.getContext(), mClassName); } } public class ActivityHandler extends AbsActivityHandler { ... protected Intent createIntent(@NonNull UriRequest request) { // 通过 Activity 生成 Intent return new Intent(request.getContext(), mClazz); } } public class DefaultActivityLauncher implements ActivityLauncher { public int startActivity(@NonNull UriRequest request, @NonNull Intent intent) { ... return startIntent(request, intent, context, requestCode, false); } /** * 启动Intent * * @param internal 是否启动App内页面 */ protected int startIntent(@NonNull UriRequest request, @NonNull Intent intent, Context context, Integer requestCode, boolean internal) { if (!checkIntent(context, intent)) { return UriResult.CODE_NOT_FOUND; } if (startActivityByAction(request, intent, internal) == UriResult.CODE_SUCCESS) { return UriResult.CODE_SUCCESS; } return startActivityByDefault(request, context, intent, requestCode, internal); } // 通过 Action 跳转 protected int startActivityByAction(@NonNull UriRequest request, @NonNull Intent intent, boolean internal) { try { final StartActivityAction action = request.getField( StartActivityAction.class, FIELD_START_ACTIVITY_ACTION); boolean result = action != null && action.startActivity(request, intent); } ... } // 通过默认方式跳转 protected int startActivityByDefault(UriRequest request, @NonNull Context context, @NonNull Intent intent, Integer requestCode, boolean internal) { try { Bundle options = request.getField(Bundle.class, FIELD_START_ACTIVITY_OPTIONS); if (requestCode != null && context instanceof Activity) { ActivityCompat.startActivityForResult((Activity) context, intent, requestCode, options); } else { ActivityCompat.startActivity(context, intent, options); } doAnimation(request); } ... } }
Router 框架梳理
按照 demo 给定的流程,不考虑自定义 UriHandler 的情况下,执行流程大致如下:
- 由 DefaultRootUriHandler 按照优先级分发到 PageAnnotationHandler、UriAnnotationHandler、RegexAnnotationHandler,若都没有匹配到,则尝试 StartUriHandler
- PageAnnotationHandler、UriAnnotationHandler、RegexAnnotationHandler 三种 UriHandler 通过 ServiceLoader 加载用户注解的类作为子 UriHandler
- 判断子 UriHandler 是否为 Activity 和 ClassName,如果是则走默认的 activity 跳转,即:DefaultActivityLauncher,不是则继续向下分发
好了,就是这些了,欢迎各位读者提问及意见 ~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Agile Web Development with Rails, Third Edition
Sam Ruby、Dave Thomas、David Heinemeier Hansson / Pragmatic Bookshelf / 2009-03-17 / USD 43.95
Rails just keeps on changing. Rails 2, released in 2008, brings hundreds of improvements, including new support for RESTful applications, new generator options, and so on. And, as importantly, we’ve a......一起来看看 《Agile Web Development with Rails, Third Edition》 这本书的介绍吧!