本文是系列文章,其他文章见:
鸿蒙@fw/router框架源码解析(一)-router页面管理
鸿蒙@fw/router框架源码解析(二)-Navigation页面管理
鸿蒙@fw/router框架源码解析(三)-Navigation页面容器封装
鸿蒙@fw/router框架源码解析(四)-路由Hvigor插件实现原理
鸿蒙@fw/router框架源码解析(六)-模块化开发如何实现代码解耦
鸿蒙@fw/router框架源码解析
介绍
@fw/router是在HarmonyOS鸿蒙系统中开发应用所使用的开源模块化路由框架。 该路由框架基于模块化开发思想设计,支持页面路由和服务路由,支持自定义装饰器自动注册,与系统路由相比使用更便捷,功能更丰富。
具体功能介绍见@fw/router:鸿蒙模块化路由框架,助力开发者实现高效模块化开发!
基于模块化的开发需求,本框架支持以下功能:
- 支持页面路由和服务路由;
- 页面路由支持多种模式(router模式,Navigation模式,混合模式);
- router模式支持打开非命名路由页面;
- 页面打开支持多种方式(push/replace),参数传递;关闭页面,返回指定页面,获取返回值,跨页面获取返回值;
- 支持服务路由,可使用路由url调用公共方法,达到跨技术栈调用以及代码解耦的目的;
- 支持页面路由/服务路由通过装饰器自动注册;
- 支持动态导入(在打开路由时才import对应har包),支持自定义动态导入逻辑;
- 支持添加拦截器(打开路由,关闭路由,获取返回值);
- Navigation模式下支持自定义Dialog对话框;
详见gitee传送门
代码解析
RouterInterceptorManager
概述
拦截器是在实际项目中使用路由框架时最为重要的功能,很多工程化、模块化逻辑都可以通过拦截器来实现。
比如,鉴权、埋点、错误统一处理、多协议支持等。
RouterInterceptorManager
拦截器管理类主要功能是实现对RouterManager
相关方法的拦截处理,并提供拦截器管理功能,支持业务代码添加自定义的拦截器。
RouterInterceptorManager
是完全独立的,RouterManager
对它并没有依赖。
RouterInterceptorManager
的拦截逻辑是通过官方Aspect
库通过方法插桩实现的。
那么如果不通过Aspect
库是否可以实现呢?也不是不行,就是要RouterManager
直接调用RouterInterceptorManager
中的同名方法,无法避免两者之间的代码依赖。
RouterInterceptor
拦截器接口定义。
export interface RouterInterceptor {
open?(request: RouterRequestWrapper): Promise<boolean>
close?(options?: RouterBackOptionsWrapper): boolean
onResponse?(request: RouterRequestWrapper, response: RouterResponse): Promise<boolean>
}
open
方法代表拦截的路由打开操作,其中request
是路由请求参数,注意参数是经过处理的RouterRequestWrapper类型。返回值代表是否拦截,如果返回true,则路由不会继续原处理逻辑。
close
方法代码拦截的关闭页面操作,其中options
是关闭页面参数,注意参数是经过处理的RouterBackOptionsWrapper类型。返回值逻辑同上。
onResponse
方法代表收到响应数据操作,其中request
是路由请求对象,response
是路由返回对象,返回值逻辑同上。
拦截器管理类和具体的拦截器都需要实现该接口。
RouterInterceptorManager-拦截器管理逻辑
export class RouterInterceptorManager implements RouterInterceptor {
private interceptors: List<RouterInterceptor> = new List()
addInterceptor(interceptor: RouterInterceptor) {
this.interceptors.add(interceptor);
}
removeInterceptor(interceptor: RouterInterceptor) {
this.interceptors.remove(interceptor)
}
}
拦截器管理类目前简单的用List管理拦截器项目。后续考虑增加优先级等排序逻辑。
RouterInterceptorManager-插桩替换原方法
export class RouterInterceptorManager implements RouterInterceptor {
replaceRouterManagerFunction() {
let originFunction: Function = RouterManager.getInstance()._realOpen.bind(RouterManager.getInstance())
util.Aspect.replace(RouterManager, '_realOpen', false, (instance: RouterManager, request: RouterRequestWrapper) => {
return new Promise<RouterResponse>((resolve, reject) => {
this.open(request).then((interrupt) => {
if (!interrupt) {
originFunction(request).then(resolve)
} else {
if (request) {
resolve({
code: RouterResponseError.RequestInterrupt.code,
msg: RouterResponseError.RequestInterrupt.msg
})
}
}
})
})
})
let originClose: Function = RouterManager.getInstance().close.bind(RouterManager.getInstance())
util.Aspect.replace(RouterManager, 'close', false, (instance: RouterManager, options?: RouterBackOptionsWrapper) => {
let interrupt = this.close(options)
if (!interrupt) {
originClose(options)
}
})
let originProcessResult: Function = RouterManager.getInstance().processResponse.bind(RouterManager.getInstance())
util.Aspect.replace(RouterManager, 'processResponse', false, (instance: RouterManager, resolve: (value: RouterResponse | PromiseLike<RouterResponse>) => void, request: RouterRequestWrapper, result: RouterResponse) => {
this.onResponse(request, result).then((interrupt) => {
if (!interrupt) {
originProcessResult(resolve, request, result)
}
})
})
}
}
该方法主要是实现RouterManager
中_realOpen
、close
、processResponse
三个方法的替换。
核心代码为:
let originFunction: Function = RouterManager.getInstance()._realOpen.bind(RouterManager.getInstance())
util.Aspect.replace(RouterManager, '_realOpen', false, (instance: RouterManager, request: RouterRequestWrapper) => { })
先保存原实现方法,然后通过util.Aspect.replace
替换方法实现;
replace方法的定义为static replace(targetClass: Object, methodName: string, isStatic: boolean, instead: Function): void;
,其中第四个参数为新方法实现,其参数和原方法的参数相比多了一个instance
。
this.open(request).then((interrupt) => {
if (!interrupt) {
originFunction(request).then(resolve)
} else {
if (request) {
resolve({
code: RouterResponseError.RequestInterrupt.code,
msg: RouterResponseError.RequestInterrupt.msg
})
}
}
})
在新的实现方法中,先调用RouterInterceptorManager
的open
方法。如果其返回值是false则执行之前保存的原实现方法。如果是true,则直接触发回调,路由的open操作失败。
close
和onResponse
的操作逻辑类似。不过路由的close方法是同步方法,且没有返回值,因此未处理返回值为true的逻辑。
RouterInterceptorManager-RouterInterceptor接口方法实现
export class RouterInterceptorManager implements RouterInterceptor {
open(request: RouterRequestWrapper): Promise<boolean> {
return new Promise<boolean>(async (resolve, reject) => {
for (const interceptor of this.interceptors) {
if (interceptor.open && await interceptor.open(request)) {
resolve(true)
return
}
}
resolve(false)
})
}
close(options?: RouterBackOptionsWrapper): boolean {
for (const interceptor of this.interceptors) {
if (interceptor.close && interceptor.close(options)) {
return true
}
}
return false
}
onResponse(request: RouterRequestWrapper, response: RouterResponse): Promise<boolean> {
return new Promise<boolean>(async (resolve, reject) => {
for (const interceptor of this.interceptors) {
if (interceptor.onResponse && await interceptor.onResponse(request, response)) {
resolve(true)
return
}
}
resolve(false)
})
}
}
实现RouterInterceptor中定义的三个方法,遍历interceptors
列表,并执行相关方法。open
和onResponse
是异步方法,但目前的设计中调用失败是通过resolve返回错误报文来实现,因此目前是简单的用await来实现遍历调用逻辑,未处理.catch操作。
总结
路由框架中的拦截器属于代码相对简单,但是作用很大的模块;如果你需要对系统路由进行二次封装,那么最好提前把路由拦截逻辑处理好,这样在之后的项目开发中会节省很多成本,大大提升实际的开发效率。