项目组件化过程当中使用了WMRouter,为了更好的理解并使用。花了一周的时间研究了一下WMRouter(v1.2.0版本)。下面从四个方面说下本身的理解,但愿能给你们提供帮助。html
路由框架,也就是路由的做用。路由是起什么做用呢?就像送快递,从小县城到省会,再从省会发到北京分拨中心,而后再从北京分拨中心发到回龙观,再从回龙观发到具体的小区。路由框架解决的就是如何从A页面跳转到B页面的问题,其会在A和B之间创建数个节点。经过这些节点依次进行转发,最终达到目的地。java
Android原生已经支持AndroidManifest去管理App跳转,为何要有路由库?android
WMRouter是一款Android路由框架,主要提供URI分发、ServiceLoader两大功能(后面会看到,URI分发功能也是用ServiceLoader实现的)git
URI分发功能可用于跨module的页面跳转、动态下发URI连接的跳转等场景,特色以下:github
WMRouter提供了ServiceLoader模块,相似Java中的
java.util.ServiceLoader
,但功能更加完善。经过ServiceLoader能够在一个App的多个模块之间经过接口调用代码,实现模块解耦,便于实现组件化、模块间通讯,以及和依赖注入相似的功能等。其特色以下:正则表达式
详细使用参见WMRouter设计与使用文档,这里就大概说下整体的流程。设计模式
buildscript {
repositories {
jcenter()
}
dependencies {
// Android Gradle插件
classpath 'com.android.tools.build:gradle:3.2.1'
// 添加WMRouter插件
classpath "com.sankuai.waimai.router:plugin:1.x"
}
}
复制代码
apply plugin: 'com.android.application'
// 应用WMRouter插件
apply plugin: 'WMRouter'
复制代码
compile 'com.sankuai.waimai.router:router:1.x'
复制代码
annotationProcessor 'com.sankuai.waimai.router:compiler:1.x'
复制代码
# 保留ServiceLoaderInit类,须要反射调用
-keep class com.sankuai.waimai.router.generated.ServiceLoaderInit { *; }
# 避免注解在shrink阶段就被移除,致使obfuscate阶段注解失效、实现类仍然被混淆
-keep @interface com.sankuai.waimai.router.annotation.RouterService
复制代码
在Application.onCreate中初始化:最简单的方式初始化方式就两行代码。数组
// 建立RootHandler
DefaultRootUriHandler rootHandler = new DefaultRootUriHandler(context);
// 初始化,必须在主线程调用
Router.init(rootHandler);
复制代码
跳转的目标Activity,添加注解@RouterUri浏览器
@RouterUri(path = "/test/schemehost", scheme = "test", host = "test.demo.com")
public class AdvancedDemoActivity extends BaseActivity {
...
}
复制代码
发起跳转有好几种方式,经常使用的有如下三种。其实最经常使用的是方式三。缓存
// 方式1,直接传context和URI
Router.startUri(context, "/account");
// 方式2,或构造一个UriRequest
Router.startUri(new UriRequest(context, "/account"));
// 方式3,使用DefaultUriRequest,最经常使用
new DefaultUriRequest(context, uri)//传入context和目标uri
// startActivityForResult使用的RequestCode
.activityRequestCode(100)
// 设置跳转来源,默认为内部跳转,还能够是来自WebView、来自Push通知等。
// 目标Activity可经过UriSourceTools区分跳转来源。
.from(UriSourceTools.FROM_INTERNAL)
// Intent加参数
.putIntentExtra("test-int", 1)
.putIntentExtra("test-string", "str")
// 设置Activity跳转动画
.overridePendingTransition(R.anim.enter_activity, R.anim.exit_activity)
// 监听跳转完成事件
.onComplete(new OnCompleteListener() {
@Override
public void onSuccess(@NonNull UriRequest request) {
ToastUtils.showToast(request.getContext(), "跳转成功");
}
@Override
public void onError(@NonNull UriRequest request, int resultCode) {
}
})
// 这里的start实际也是调用了Router.startUri方法
.start();
复制代码
上面5步只是最基本的URI分发功能使用,SDK还提供了不少设置,方便在实际项目中使用。另外,在使用过程当中,还有一些比较容易遗漏的点。下面进行详细说明。
注意:若是项目配置的Android Gradle插件版本比WMRouter依赖的版本低,默认会覆盖为高版本(可经过./gradlew buildEnvironment
命令查看classpath的依赖关系)。若是不但愿被覆盖,能够尝试把配置改为:
classpath("com.sankuai.waimai.router:plugin:1.x") {
exclude group: 'com.android.tools.build'
}
复制代码
若是使用了@RouterService注解和ServiceLoader加载实例的功能,会反射调用构造方法,应根据实际状况配置Proguard,避免实现类中的构造方法被移除,示例以下。
# 使用了RouterService注解的实现类,须要避免Proguard把构造方法、方法等成员移除(shrink)或混淆(obfuscate),致使没法反射调用。实现类的类名能够混淆。
-keepclassmembers @com.sankuai.waimai.router.annotation.RouterService class * { *; }
复制代码
// CustomFactory.java--自定义工厂
IFactoryService service4 = Router.getService(IFactoryService.class, "/factory", new IFactory() {
@NonNull
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
return clazz.getConstructor(String.class).newInstance("CreateByCustomFactory");
}
});
复制代码
DefaultRootUriHandler rootHandler = new DefaultRootUriHandler(context);
//设置全局跳转完成的监听,能够在其中跳转失败时执行全局降级逻辑。
//在DefaultRootUriHandler中默认配置的GlobalOnCompleteListener会在跳转失败时弹Toast提示用户
rootHandler.setGlobalOnCompleteListener();
// 自定义Logger
DefaultLogger logger = new DefaultLogger() {
@Override
protected void handleError(Throwable t) {
super.handleError(t);
// 此处上报Fatal级别的异常
}
};
// 设置Logger
Debugger.setLogger(logger);
// Log开关,建议测试环境下开启,方便排查问题。
Debugger.setEnableLog(true);
// 调试开关,建议测试环境下开启。调试模式下,严重问题直接抛异常,及时暴漏出来。
Debugger.setEnableDebug(true);
Router.init(rootHandler);
// 后台线程懒加载
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void[] objects) {
Router.lazyInit();
return null;
}
}.execute();
复制代码
/** 无效来源 */
public static final int FROM_INVALID = 0;
/** 外部跳转 */
public static final int FROM_EXTERNAL = FROM_INVALID + 1;
/** 内部跳转*/
public static final int FROM_INTERNAL = FROM_EXTERNAL + 1;
/** 从WebView跳转 */
public static final int FROM_WEBVIEW = FROM_INTERNAL + 1;
/** 从Push跳转 */
public static final int FROM_PUSH = FROM_WEBVIEW + 1;
复制代码
最经常使用,基本只用这个注解就能够知足URI分发需求。根据URI的scheme+host,寻找并分发给对应的PathHandler,以后PathHandler再根据path匹配RouterUri注解配置的节点。可用于Activity或UriHandler的非抽象子类(Activity也会被转化成UriHandler,在Activity中能够经过Intent.getData()
获取到URI)
参数以下:
说明:
WMRouter支持多scheme+host+path的跳转,也支持只有path的跳转。若是RouterUri中配置了scheme、host、path,则跳转时应使用scheme+host+path的完整路径;若是RouterUri中只配置了path,则跳转应直接使用path。
因为多数场景下每每只须要一个固定的scheme+host,不想在每一个RouterUri注解上都写一遍scheme、host,这种场景能够在初始化时用new DefaultRootUriHandler("scheme", "host")
指定默认的scheme、host,RouterUri没有配置的字段会使用这个默认值。
一、用户帐户页面只配置path;跳转前要先登陆,所以添加了一个LoginInterceptor。
@RouterUri(path = "/account", interceptors = LoginInterceptor.class)
public class UserAccountActivity extends Activity {
}
复制代码
Router.startUri(context, "/account");
复制代码
二、一个页面配置多个path。
@RouterUri(scheme = "demo_scheme", host = "demo_host", path = {"/path1", "/path2"})
public class TestActivity extends Activity {
}
复制代码
Router.startUri(context, "demo_scheme://demo_host/path1");
Router.startUri(context, "demo_scheme://demo_host/path2");
复制代码
三、根据后台下发的ABTest策略,同一个连接跳转不一样的Activity。其中AbsActivityHandler是WMRouter提供的用于跳转Activity的UriHandler通用基类。
@RouterUri(path = "/home")
public class HomeABTestHandler extends AbsActivityHandler {
@NonNull
@Override
protected Intent createIntent(@NonNull UriRequest request) {
if (FakeABTestService.getHomeABStrategy().equals("A")) {
return new Intent(request.getContext(), HomeActivityA.class);
} else {
return new Intent(request.getContext(), HomeActivityB.class);
}
}
}
复制代码
Router.startUri(context, "/home");
复制代码
RouterRegex注解也能够用于Activity和UriHandler,经过正则进行URI匹配。
参数以下:
一、对于指定域名的http(s)连接,使用特定的WebViewActivity打开。
@RouterRegex(regex = "http(s)?://(.*\\.)?(meituan|sankuai|dianping)\\.(com|info|cn).*", priority = 2)
public class WebViewActivity extends BaseActivity {
}
复制代码
二、对于其余http(s)连接,使用系统浏览器打开。
@RouterRegex(regex = "http(s)?://.*", priority = 1)
public class SystemBrowserHandler extends UriHandler {
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return true;
}
@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(request.getUri());
request.getContext().startActivity(intent);
callback.onComplete(UriResult.CODE_SUCCESS);
} catch (Exception e) {
callback.onComplete(UriResult.CODE_ERROR);
}
}
}
复制代码
RouterPage注解用于指定内部页面跳转,和RouterUri注解相比,RouterPage注解对应的scheme和host为固定的wm_router://page
,不可配置,exported为false也不可配置。感受这个是因为历史缘由存在的一个注解。本质和RouterUri注解是同样的。咱们本身的项目不会用到这个。因此不详细介绍了。有兴趣的小伙伴能够自行查看WMRouter设计与使用文档
/** 跳转到系统自带浏览器 */
@RouterRegex(regex = DemoConstant.HTTP_URL_REGEX)
public class SystemBrowserHandler extends UriHandler {
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return true;
}
@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(request.getUri());
request.getContext().startActivity(intent);
callback.onComplete(UriResult.CODE_SUCCESS);
} catch (Exception e) {
callback.onComplete(UriResult.CODE_ERROR);
}
}
}
复制代码
// 建立RootHandler
DefaultRootUriHandler rootHandler = new DefaultRootUriHandler(context);
rootHandler.addChildHandler(new UriHandler() {
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return false;
}
@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
}
});
// 初始化,必须在主线程调用
Router.init(rootHandler);
复制代码
UriInterceptor为拦截器,不作最终的URI跳转操做,但能够在最终的跳转前进行各类同步/异步操做,常见操做举例以下:
每一个UriHandler均可以添加若干UriInterceptor。在UriHandler基类中,handle()方法先调用抽象方法shouldHandle()
判断是否要处理UriRequest,若是须要处理,则逐个执行Interceptor,最后再调用handleInternal()
方法进行跳转操做。 举例来讲,跳转某些页面须要先登陆,能够实现一个LoginInterceptor以下。
public class LoginInterceptor implements UriInterceptor {
@Override
public void intercept(@NonNull UriRequest request, @NonNull final UriCallback callback) {
final FakeAccountService accountService = FakeAccountService.getInstance();
if (accountService.isLogin()) {
// 已经登陆,不需处理,继续跳转流程
callback.onNext();
} else {
// 没登陆,提示登陆并启动登陆页
Toast.makeText(request.getContext(), "请先登陆~", Toast.LENGTH_SHORT).show();
accountService.registerObserver(new FakeAccountService.Observer() {
@Override
public void onLoginSuccess() {
accountService.unregisterObserver(this);
// 登陆成功,继续跳转
callback.onNext();
}
@Override
public void onLoginFailure() {
accountService.unregisterObserver(this);
// 登陆失败,终止流程,返回错误ResultCode
callback.onComplete(CustomUriResult.CODE_LOGIN_FAILURE);
}
});
// 启动登陆页
startActivity(request.getContext(), LoginActivity.class);
}
}
}
复制代码
RootUriHandler
的startUri
开始分发的,因此,若是要添加全局的拦截器,就能够经过给RootUriHandler
的子类实例对象,好比DefaultRootUriHandler
对象添加拦截器的方式实现。根据实际状况,能够自定义具备各类功能的UriHandler和UriInterceptor,前面已经提到,再也不赘述。通常使用DefaultRootHandler和DefaultUriRequest,以及少许自定义的UriHandler已经能够知足绝大多数需求。若是有更复杂的场景须要,WMRouter中的核心组件能够经过继承、组合等方式实现更灵活的定制。例如自定义RootUriHandler示例以下:
// 自定义RootUriHandler
public class CustomRootUriHandler extends RootUriHandler {
// ...
public CustomRootUriHandler() {
// 添加Uri注解支持
addHandler(new UriAnnotationHandler());
// 添加一个自定义的HttpHandler
addHandler(new CustomHttpHandler());
}
}
// 自定义UriRequest
public class CustomUriRequest extends UriRequest {
// ...
public CustomUriRequest setCustomProperties(String s) {
putField("custom_properties", s);
return this;
}
}
// 初始化
Router.init(new CustomRootUriHandler());
// 启动Uri
CustomUriRequest request = new CustomUriRequest(mContext, url)
.setCustomProperties("xxx");
Router.startUri(request);
复制代码
public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) {
super(context);
mPageAnnotationHandler = createPageAnnotationHandler();
mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
mRegexAnnotationHandler = createRegexAnnotationHandler();
// 按优先级排序,数字越大越先执行
// 处理RouterPage注解定义的内部页面跳转,若是注解没定义,直接结束分发
addChildHandler(mPageAnnotationHandler, 300);
// 处理RouterUri注解定义的URI跳转,若是注解没定义,继续分发到后面的Handler
addChildHandler(mUriAnnotationHandler, 200);
// 处理RouterRegex注解定义的正则匹配
addChildHandler(mRegexAnnotationHandler, 100);
// 添加其余用户自定义Handler...
// 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
addChildHandler(new StartUriHandler(), -100);
// 全局OnCompleteListener,用于输出跳转失败提示信息
setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);
}
复制代码
经过查看源码发现,全部Activity类型的UriHandler(就是经过在Activity类名上面添加注解,从而经过UriTargetTools的toHandler方法,生成的UriHandler实例),路由分发的最后一步(跳转该activity),都是经过ActivityLauncher接口的startActivity方法执行的。而SDK提供了ActivityLauncher接口的默认实现类DefaultActivityLauncher。咱们能够在这里hook一些核心的方法,执行本身的跳转逻辑。好比下面的例子,跳转到Activity以前,判断intent中的context是否是Activity类型的,若是不是,那么加上Intent.FLAG_ACTIVITY_NEW_TASK
。
public class XinActivityLauncher extends DefaultActivityLauncher {
//...省略代码
@Override
protected int startActivityByDefault(UriRequest request, Context context, Intent intent, Integer requestCode, boolean internal) {
try {
Bundle options = (Bundle)request.getField(Bundle.class, FIELD_START_ACTIVITY_OPTIONS);
if (requestCode != null && context instanceof Activity) {
ActivityCompat.startActivityForResult((Activity)context, intent, requestCode, options);
} else {
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
ActivityCompat.startActivity(context, intent, options);
}
this.doAnimation(request);
if (internal) {
request.putField(FIELD_STARTED_ACTIVITY, 1);
Debugger.i(" internal activity started, request = %s", new Object[]{request});
} else {
request.putField(FIELD_STARTED_ACTIVITY, 2);
Debugger.i(" external activity started, request = %s", new Object[]{request});
}
return 200;
} catch (ActivityNotFoundException var7) {
Debugger.w(var7);
return 404;
} catch (SecurityException var8) {
Debugger.w(var8);
return 403;
}
}
//...省略代码
}
复制代码
简单来讲,ServiceLoader的核心做用就是:根据接口(或抽象类)名,找到接口(或抽象类)的具体实例,若是一个接口对应多个实例,那么再根据不一样实例的key找到具体的接口实例。
在实现组件化的项目中,极可能多个业务module之间是没有依赖关系的可是确实有可能不一样的业务module之间仍是有业务逻辑的耦合的。好比:
ServiceLoader模块使用主要分三步:
经过RouterService注解声明实现类所实现的接口(或继承的父类,例如Activity、Fragment、Object等,后文再也不重复说明),一个接口能够有多个实现类,一个类也能够同时实现多个接口。RouterService注解的参数以下:
示例以下:
public interface IService {
}
@RouterService(interfaces = IService.class, key = 'key1')
public static class ServiceImpl1 implements IService {
}
@RouterService(interfaces = IService.class, key = 'key2', singleton = true)
public static class ServiceImpl2 implements IService {
}
复制代码
能够直接获取实现类的Class,例如获取Activity的Class进行页面跳转。
Class<IService> clazz = Router.getServiceClass(IService.class, "key1");
复制代码
List<Class<IService>> classes = Router.getAllServiceClasses(IService.class);
复制代码
ServiceLoader更常见的使用场景,是获取实现类的实例而不是Class。实现类的构造在ServiceLoader中最终由Factory实现,构造失败会返回null或空数组。
// 使用无参构造函数
IService service = Router.getService(IService.class, "key1");
List<IService> list = Router.getAllServices(IService.class);
复制代码
// 使用Context参数构造
IService service = Router.getService(IService.class, context);
List<IService> list = Router.getAllServices(IService.class, context);
复制代码
对于实现类有特殊构造函数的状况,能够经过Factory自行从class获取构造方法进行构造,示例以下:
// 使用自定义Factory
IFactory factory = new IFactory() {
public Object create(Class clazz) {
return clazz.getConstructor().newInstance();
}
};
IService service = Router.getService(IService.class, factory);
List<IService> list = Router.getAllServices(IService.class, factory);
复制代码
在声明实现类时,能够在类中定义一个返回值类型为该实现类且无参数的静态方法,并使用RouterProvider注解标注。当调用Router获取实例时,若是没有指定Factory,则优先调用Provider方法获取实例,找不到Provider再使用无参数构造。使用示例以下:
@RouterService(interfaces = IService.class, key = 'key', singleton = true)
public static class ServiceImpl implements IService {
public static final ServiceImpl INSTANCE = new ServiceImpl();
// 使用注解声明该方法是一个Provider
@RouterProvider
public static ServiceImpl provideInstance() {
return INSTANCE;
}
}
// 调用时不传Factory,优先找Provider,找不到再使用无参数构造
IService service = Router.getService(IService.class, "key");
List<IService> list = Router.getAllServices(IService.class);
复制代码
注解声明为singleton的单例实现类,在调用getService()/getAllServices()
方式获取实例时,实例会由单例缓存池管理,WMRouter中不会重复构造,且线程安全。
注意:当经过ServiceLoader获取Class、直接调用等其余方式使用实现类时,应避免重复建立对象,不然会致使单例失效。能够结合Provider确保实例不会重复建立。
WMRouter的核心原理大概就是,经过注解标注路由信息,在编译期动态扫描路由信息,生成加载路由表信息的java类。并利用 gradle transform和asm生成加载所有路由信息的class文件。在app运行时,路由框架反射调用这个class文件,从而完成了路由表的装载。
ServiceInit_*
类,UriAnnotationInit_*
类等辅助注册代码。首先,编译的时候,根据@RouterUri
,@RouterRegex
,@RouterPage
,@RouterService
注解,生成辅助代码。详细的文件见下图。具体的生成原理参见路由节点的动态生成
这里须要注意,@RouterUri
,@RouterRegex
,@RouterPage
这三个注解,会同时生成两个文件。以@RouterUri
为例进行说明:
UriAnnotationInit_**
类,其init
方法中的每一行,都是本module中使用@RouterUri
注解的类的注册到UriAnnotationHandler
的执行代码。所谓注册,其实质就是创建映射关系。须要注意UriAnnotationHandler这个注册过程,一旦执行,就会开启整个UriAnnotationHandler
这个分支的全部注册过程。ServiceInit_**
类,其init
方法中,经过ServiceLoader.put()
方法,创建了接口抽象类(IUriAnnotationInit.class
),接口实现类(com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d.class
),接口实现类的key(com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d
),这三者之间的映射关系。还须要注意。编译过程只是生成了可以进行注册的代码。可是代码并无执行。只有等到开启提早加载(执行Router.lazyInit()
),或者开启跳转(Router#startUri(com.sankuai.waimai.router.core.UriRequest)
)的时候,才开始注册。
public class UriAnnotationInit_72565413b8384a4bebb02d352762d60d implements IUriAnnotationInit {
public void init(UriAnnotationHandler handler) {
handler.register("", "", "/advanced_demo", "com.sankuai.waimai.router.demo.advanced.AdvancedDemoActivity", false);
}
}
复制代码
public class ServiceInit_eb71854fbd69455ef4e0aa026c2e9881 {
public static void init() {
ServiceLoader.put(IUriAnnotationInit.class, "com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d", com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d.class, false);
}
}
复制代码
ServiceLoaderInit
类ServiceLoaderInit
类,只有一个init
方法。其中包含了整个项目全部module的ServiceInit_**
辅助类的init
执行代码。ServiceInit_**
辅助类是上面经过注解生成的。ServiceLoaderInit
类的init
方法。为何呢?上面咱们说到,注解只是生成辅助代码,可是并无执行。只有执行了ServiceLoaderInit
类的init
方法,才会真正的执行路由表注册代码,创建真正的映射关系。另外须要注意,改方法执行后,只会创建第一层关系。只有等到开启提早加载(执行Router.lazyInit()
),或者开启跳转(Router#startUri(com.sankuai.waimai.router.core.UriRequest)
)的时候,才开始执行UriAnnotationHandler
等子节点分支的注册代码(initAnnotationConfig()
),创建映射关系。public class ServiceLoaderInit {
public static void init() {
ServiceInit_aea7f96d0419b507d9b0ef471913b2f5.init();
ServiceInit_f3649d9f5ff15a62b844e64ca8434259.init();
ServiceInit_eb71854fbd69455ef4e0aa026c2e9881.init();
ServiceInit_b57118238b4f9112ddd862e55789c834.init();
ServiceInit_f1e07218f6691f962a9f674eb5b4b8bd.init();
ServiceInit_e694d982fb5d7a3a8c6b7085829e74a6.init();
ServiceInit_ee5f6404731417fe1433da40fd3c9708.init();
ServiceInit_9482ef47a8cf887ff1dc4bf705d5fc0a.init();
ServiceInit_36ed390bf4b81a8381d45028b37cc645.init();
}
}
复制代码
Router#startUri(com.sankuai.waimai.router.core.UriRequest)
开启跳转getRootHandler().startUri(request)
;其中getRootHandler()方法获取RootUriHandler的实例,默认是DefaultRootUriHandler对象。RootUriHandler#startUri
方法。UriHandler#handle
方法开始分发。/** * 处理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 && !request.isSkipInterceptors()) {
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();
}
}
复制代码
UriHandler#handle
方法中,首先调用UriHandler#shouldHandle
方法,判断是否应该处理。这里要注意,UriHandler#handle
在各个UriHanlder的子类中都会调用(责任链模式)。UriHandler
的具体实例是什么
UriHandler#handle
方法,执行第一句代码shouldHandle。UriHandler#handle
方法的第一句shouldHandle,执行的是ChainedHandler的shouldHandle方法。该方法会判断mHandlers是否为空,在DefaultRootUriHandler的构造函数里面,已经添加了UriAnnotationHandler,PageAnnotationHandler,RegexAnnotationHandler,StartUriHandler等UriHandler对象。因此,shouldHandle返回true,继续向下执行。public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) {
super(context);
mPageAnnotationHandler = createPageAnnotationHandler();
mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
mRegexAnnotationHandler = createRegexAnnotationHandler();
// 按优先级排序,数字越大越先执行
// 处理RouterPage注解定义的内部页面跳转,若是注解没定义,直接结束分发
addChildHandler(mPageAnnotationHandler, 300);
// 处理RouterUri注解定义的URI跳转,若是注解没定义,继续分发到后面的Handler
addChildHandler(mUriAnnotationHandler, 200);
// 处理RouterRegex注解定义的正则匹配
addChildHandler(mRegexAnnotationHandler, 100);
// 添加其余用户自定义Handler...
// 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
addChildHandler(new StartUriHandler(), -100);
// 全局OnCompleteListener,用于输出跳转失败提示信息
setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);
}
@Override
public void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {
mInitHelper.ensureInit();
super.handle(request, callback);
}
复制代码
ChainedHandler{
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return !mHandlers.isEmpty();
}
@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);
}
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode);
}
});
} else {
callback.onNext();
}
}
}
复制代码
public class UriAnnotationHandler extends UriHandler {
/** * 经过scheme+host找对应的PathHandler,找到了才会处理 */
private PathHandler getChild(@NonNull UriRequest request) {
return mMap.get(request.schemeHost());
}
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
return getChild(request) != null;
}
@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
PathHandler pathHandler = getChild(request);
if (pathHandler != null) {
pathHandler.handle(request, callback);
} else {
// 没找到的继续分发
callback.onNext();
}
}
}
复制代码
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);
}
}
}
复制代码
public class UriAnnotationInit_179aab35b2125f96c7b066a0a2eccf82 implements IUriAnnotationInit {
public void init(UriAnnotationHandler handler) {
handler.register("", "", "/service_loader", "com.sankuai.waimai.router.demo.lib2.advanced.ServiceLoaderActivity", false);
handler.register("", "", "/lib2", "com.sankuai.waimai.router.demo.lib2.basic.DemoLibActivity2", false);
}
}
复制代码
UriHandler就是咱们所说的节点。UriHandler用于处理URI跳转请求,能够嵌套从而逐层分发和处理请求。UriHandler是异步结构,接收到UriRequest后处理(例如跳转Activity等),若是处理完成,则调用
callback.onComplete()
并传入ResultCode;若是没有处理,则调用callback.onNext()
继续分发。
UriHandler及其实现类的类图整理以下(简单起见,省略了部分实现):
UriHandler
是抽象类,其核心在于handle()
方法,另外,shouldHandle()
和handleInternal()
方法是抽象方法,由子类去实现。shouldHandle()
方法判断是否应该在当前UriHandler执行分发。handleInternal()
是当前UriHandler执行分发的具体逻辑。RootUriHandler
的startUri()
方法,是全部分发跳转的入口。DefaultRootUriHandler
是RootUriHandler
的默认实现,其构造函数中添加了PageAnnotationHandler
,UriAnnotationHandler
,RegexAnnotationHandler
,StartUriHandler
的实例。而后分发跳转请求的时候,依次交给这四个UriHandler处理,若是某个UriHandler不可以处理该请求,则交给下一个执行。若是可以处理该请求,则交给该UriHandler的子节点继续分发。好比,若是UriAnnotationHandler可以处理该请求,会交给其子节点PathHandler处理,而后PathHandler交给ActivityHandler处理。PageAnnotationHandler
,UriAnnotationHandler
,RegexAnnotationHandler
这三个子类复写了UriHandler
的handle()
方法。其复写的目的是为了在真正开始分发以前,调用mInitHelper.ensureInit()
,确保该分支的路由表已经生成。PageAnnotationHandler
写死了SCHEME和HOST,因此只处理全部格式为 wm_router://page/*
的URI,根据path匹配。UriAnnotationHandler
分发到PathHandler
,PathHandler
分发到ActivityClassNameHandler
或ActivityHandler
是最经常使用的分发路径。PageAnnotationHandler
,UriAnnotationHandler
,RegexAnnotationHandler
及其子节点,都没有处理某个路由请求(处理的意思是:执行了UriCallback的onComplete回调),则交给StartUriHandler
处理。StartUriHandler
会从路由请求UriRequest中获取uri等参数,直接经过intent跳转。UriRequest中包含Context、URI和Fields,其中Fields为HashMap<String, Object>,能够经过Key存听任意数据。简单起见,UriRequest类同时承担了Response的功能,跳转请求的结果,也会被保存到Fields中。
每次URI跳转请求会有一个ResultCode(相似HTTP请求的ResponseCode),表示跳转结果,也存放在Fields中。常见Code以下,用户也能够自定义Code,为了不冲突,自定义Code应使用负数值。
总结来讲,UriRequest用于实现一次URI跳转中全部组件之间的通讯功能。SDK默认提供了DefaultUriRequest,通常用其就能够完成平常需求。
public class UriAnnotationInit_179aab35b2125f96c7b066a0a2eccf82 implements IUriAnnotationInit {
public void init(UriAnnotationHandler handler) {
handler.register("", "", "/service_loader", "com.sankuai.waimai.router.demo.lib2.advanced.ServiceLoaderActivity", false);
handler.register("", "", "/lib2", "com.sankuai.waimai.router.demo.lib2.basic.DemoLibActivity2", false);
}
}
复制代码
/** * 使用ServiceLoader加载注解配置 * * Created by jzj on 2018/4/28. */
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);
}
}
}
复制代码
上面说过,ServiceLoader的原理是首先经过接口名获取ServiceLoader实例,而后再从ServiceLoader实例中,根据key找到对应的接口实现类。那么具体过程呢?
put
方法(见下图),参数有:接口的Class对象,接口实现类中的key,接口实现类的Class对象,以及接口实现类是否要求单例。在put方法中,调用SERVICES.put(interfaceClass, loader)
,存入接口Class对象和ServiceLoader实例的关系。而后调用loader.putImpl(key, implementClass, singleton)
。putImpl
方法中,调用mMap.put(key, new ServiceImpl(key, implementClass, singleton))
,在ServiceLoader实例中存入key和接口实例class对象的关系。注意这里建立了一个ServiceImpl对象。那么ServiceImpl是干什么的呢?
getService
等方法获取接口的实例。该方法就一行代码ServiceLoader.load(clazz).get(key)
。load
方法中,首先调用sInitHelper.ensureInit()
。经过反射,调用com.sankuai.waimai.router.generated.ServiceLoaderInit
类的init
方法(ServiceLoaderInit
类是经过gradle插件生成的)。在init
方法中调用各个ServiceInit_36ed390bf4b81a8381d45028b37cc645
的init
方法(见下图)。那么ServiceLoaderInit类中的init方法中的这些ServiceInit_36ed390bf4b81a8381d45028b37cc645
类是什么呢?这些类中的init方法里面又是什么呢?load
方法,经过SERVICES.get(interfaceClass)
获取接口对应的ServiceLoader实例。public class ServiceLoader<I> {
//...省略部分代码
//保存了接口类名和其对应的ServiceLoader实例的对应关系。
private static final Map<Class, ServiceLoader> SERVICES = new HashMap<>();
//保存了key和接口的实现类的对应关系。
private HashMap<String, ServiceImpl> mMap = new HashMap<>();
private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
@Override
protected void doInit() {
try {
// 反射调用Init类,避免引用的类过多,致使main dex capacity exceeded问题
Class.forName(Const.SERVICE_LOADER_INIT)
.getMethod(Const.INIT_METHOD)
.invoke(null);
Debugger.i("[ServiceLoader] init class invoked");
} catch (Exception e) {
Debugger.fatal(e);
}
}
};
/** * 提供给InitClass使用的初始化接口 * * @param interfaceClass 接口类 * @param implementClass 实现类 */
public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
ServiceLoader loader = SERVICES.get(interfaceClass);
if (loader == null) {
loader = new ServiceLoader(interfaceClass);
SERVICES.put(interfaceClass, loader);
}
loader.putImpl(key, implementClass, singleton);
}
private void putImpl(String key, Class implementClass, boolean singleton) {
if (key != null && implementClass != null) {
mMap.put(key, new ServiceImpl(key, implementClass, singleton));
}
}
/** * 根据接口获取 {@link ServiceLoader} */
@SuppressWarnings("unchecked")
public static <T> ServiceLoader<T> load(Class<T> interfaceClass) {
sInitHelper.ensureInit();
if (interfaceClass == null) {
Debugger.fatal(new NullPointerException("ServiceLoader.load的class参数不该为空"));
return EmptyServiceLoader.INSTANCE;
}
ServiceLoader service = SERVICES.get(interfaceClass);
if (service == null) {
synchronized (SERVICES) {
service = SERVICES.get(interfaceClass);
if (service == null) {
service = new ServiceLoader(interfaceClass);
SERVICES.put(interfaceClass, service);
}
}
}
return service;
}
/** * 建立指定key的实现类实例,使用 {@link RouterProvider} 方法或无参数构造。对于声明了singleton的实现类,不会重复建立实例。 * * @return 找不到或获取、构造失败,则返回null */
public static <I, T extends I> T getService(Class<I> clazz, String key) {
return ServiceLoader.load(clazz).get(key);
}
/** * 建立指定key的实现类实例,使用 {@link RouterProvider} 方法或无参数构造。对于声明了singleton的实现类,不会重复建立实例。 * * @return 可能返回null */
public <T extends I> T get(String key) {
return createInstance(mMap.get(key), null);
}
}
复制代码
外观模式提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
com.sankuai.waimai.router.Router
这个类就使用了外观模式。其
lazyInit()
,
startUri(UriRequest request)
,
getService(Class<I> clazz, String key)
等方法,都是经过调用SDK内部的其余子系统提供的功能实现的。
public class Router {
/** * 此初始化方法的调用不是必须的。 * 使用时会按需初始化;但也能够提早调用并初始化,使用时会等待初始化完成。 * 本方法线程安全。 */
public static void lazyInit() {
ServiceLoader.lazyInit();
getRootHandler().lazyInit();
}
public static void startUri(UriRequest request) {
getRootHandler().startUri(request);
}
/** * 建立指定key的实现类实例,使用 {@link RouterProvider} 方法或无参数构造。对于声明了singleton的实现类,不会重复建立实例。 * @return 找不到或获取、构造失败,则返回null */
public static <I, T extends I> T getService(Class<I> clazz, String key) {
return ServiceLoader.load(clazz).get(key);
}
...
}
复制代码
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
public class DefaultActivityLauncher implements ActivityLauncher {
public static final DefaultActivityLauncher INSTANCE = new DefaultActivityLauncher();
}
复制代码
LazyInitHelper的performInit()方法,用到了双重校验锁的思想。保证只初始化一遍。
public abstract class LazyInitHelper {
private void performInit() {
if (!mHasInit) {
synchronized (this) {
if (!mHasInit) {
mHasInit = true;
long ts = 0;
final boolean enableLog = Debugger.isEnableLog();
if (enableLog) {
ts = SystemClock.uptimeMillis();
}
try {
doInit();
} catch (Throwable t) {
Debugger.fatal(t);
}
if (enableLog) {
Debugger.i("%s init cost %s ms", mTag,
SystemClock.uptimeMillis() - ts);
}
}
}
}
}
}
复制代码
/** * 单例缓存 * * Created by jzj on 2018/3/29. */
public class SingletonPool {
private static final Map<Class, Object> CACHE = new HashMap<>();
@SuppressWarnings("unchecked")
public static <I, T extends I> T get(Class<I> clazz, IFactory factory) throws Exception {
if (clazz == null) {
return null;
}
if (factory == null) {
factory = RouterComponents.getDefaultFactory();
}
Object instance = getInstance(clazz, factory);
Debugger.i("[SingletonPool] get instance of class = %s, result = %s", clazz, instance);
return (T) instance;
}
@NonNull
private static Object getInstance(@NonNull Class clazz, @NonNull IFactory factory) throws Exception {
Object t = CACHE.get(clazz);
if (t != null) {
return t;
} else {
synchronized (CACHE) {
t = CACHE.get(clazz);
if (t == null) {
Debugger.i("[SingletonPool] >>> create instance: %s", clazz);
t = factory.create(clazz);
//noinspection ConstantConditions
if (t != null) {
CACHE.put(clazz, t);
}
}
}
return t;
}
}
}
复制代码
工厂方法模式(Factory Method Pattern),在实际开发过程当中咱们都习惯于直接使用 new 关键字用来建立一个对象,但是有时候对象的创造须要一系列的步骤:你可能须要计算或取得对象的初始设置;选择生成哪一个子对象实例;或在生成你须要的对象以前必须先生成一些辅助功能的对象,这个时候就须要了解该对象建立的细节,也就是说使用的地方与该对象的实现耦合在了一块儿,不利于扩展,为了解决这个问题就须要用到咱们的工厂方法模式,它适合那些建立复杂的对象的场景,工厂方法模式也是一个使用频率很高的设计模式
/** * 从Class构造实例 */
public interface IFactory {
@NonNull
<T> T create(@NonNull Class<T> clazz) throws Exception;
}
复制代码
public class ContextFactory implements IFactory {
private final Context mContext;
public ContextFactory(Context context) {
mContext = context;
}
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
return clazz.getConstructor(Context.class).newInstance(mContext);
}
}
复制代码
public class EmptyArgsFactory implements IFactory {
public static final EmptyArgsFactory INSTANCE = new EmptyArgsFactory();
private EmptyArgsFactory() {
}
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
return clazz.newInstance();
}
}
复制代码
经过工厂方法获取实例
public class ServiceLoader<I> {
private <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) {
//...省略部分代码
Class<T> clazz = (Class<T>) impl.getImplementationClazz();
//经过工厂方法获取实例
T t = factory.create(clazz);
Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t);
return t;
}
}
复制代码
责任链模式是一种对象的行为模式。在责任链模式里,不少对象由每个对象对其下家的引用而链接起来造成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪个对象最终处理这个请求,这使得系统能够在不影响客户端的状况下动态地从新组织和分配责任。
succeesor
表示的是责任链中的下一个Handler
ChainedHandler
其实是DefaultRootUriHandler
,由于DefaultRootUriHandler
是ChainedHandler
的子类,并且没有实现handleInternal
方法。因此调用的是ChainedHandler的handleInternal方法,最终调用的是next(Iterator<UriHandler> iterator,UriRequest request,UriCallback callback)
方法。而该方法中,在某个节点的onNext回调里面又递归调用了其本身,这样就创建了链式关系。public class ChainedHandler extends UriHandler {
@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);
}
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode);
}
});
} else {
callback.onNext();
}
}
}
复制代码
写到这里,有点心虚,毕竟不是本身写的框架,也不知道当时别人开发的时候的思路。只能说大致上猜想一下。
关于这个框架诞生的需求背景,感受美团外卖Android平台化架构演进实践和WMRouter:美团外卖Android开源路由框架说的很详细,你们能够学习一下,在遇到问题的时候怎么选择解决方案,怎样设计架构。
关于通用的路由需求,感受这篇文章说的挺好,你们能够学习一下Android 组件化 —— 路由设计最佳实践