内容简介:上周四美团外卖技术团队开源了一个 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,不是则继续向下分发
好了,就是这些了,欢迎各位读者提问及意见 ~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。