面向切面编程在java中是很常见的运用,它可以很容易的在不改动已有代码的情况下加入自己的代码逻辑。 一些jdk核心代码,我们没有办法直接编辑,也可以通过这种方式进行修改。在Java中做到这些很容易,因为Java有一整套反射机制。
在Flutter中,做到这些就有些困难,为了更好的性能,Flutter中的Dart阉割了它的反射功能。
在过往的时间里也出现了一些可以用来做Flutter Aop的工具:
1.XianyuTech/aspectd
最早是闲鱼团队发起的,但是已经很久没有更新,SDK支持版本仍然停留在2.5。
2.Beike_AspectD
贝壳团队在Aspectd基础上进行了改造和适配,但最新SDK也只支持到3.10.5
3.sa_flutter_aspectd
神策团队基于aspectd做的一些改造,专门为它们信息采集做服务,但更新记录只停留在2022年。
4.aspect_frontend_server
它是我在得知aspectd不支持2.5后续版本后,借鉴了aspectd代码,并重写的一套Flutter Aop面向切面编程框架,从2021年开始更新一直至今,已经支持最新的flutter 3.38.1。
aspect_frontend_server虽然是借鉴了aspectd代码的代码,但可以说是完全重写,它去掉了很多原本aspectd中的功能,只保留了最基本的aop功能,也就是拦截某个方法的运行,可以在它运行的前后插入自己的运行逻辑。设置上完全替换到原有逻辑。
aspect_frontend_server的集成方法也很简单。只需要替换到原本flutter中的frontend_server_aot.dart.snapshot文件,不需要引入任何额外的库。
通过aspect_frontend_server写入一个注入例子也很简单,下面是一个简单的例子:
在mian.dart文件中我们写入:
dart
import 'inject.dart';
在injiect.dart中写入:
dart
@pragma('vm:entry-point')
@pragma("aopd:inject", {
//注入的包名
"importUri": "package:flutter/src/gestures/binding.dart",
//注入的类名
"clsName": "GestureBinding",
//注入的方法名
"methodName": "-dispatchEvent",
//是否符号匹配
"isRegex": false
})
//必须是static,不然不起作用
static dynamic dispatchEvent(
Object target,
String functionName,
List<dynamic> positionalParams,
Map<dynamic, dynamic> namedParams,
Function proceed) {
PointerEvent event = positionalParams[0];
HitTestResult? hitTestResult = positionalParams[1];
debugPrint('dispatchEvent - start ${event.kind.name}');
return proceed.call(event, hitTestResult);
}
上面的例子可以拦截所有Flutter底层传递给应用层的触摸事件,如果我们需要分析用户的行为,通过在这个函数上插桩就可以很容易实现。
aspect_frontend_server的用法还有很多,例如静态方法插桩,mixin插桩,extension插桩等等。可以自己下载项目运行example了解。
aspect_frontend_server相对于aspectd有很大不同:
- aspectd不支持flutter 2.5.4以上,本项目最高支持到2.38.1
- aspectd使用前需要对flutter tools的代码进行修改,本项目只需要替换flutter sdk对应的frontend_server.dart.snapshot即可
- aspectd的实现原理过于复杂,本项目去掉了Call,Inject等用法保留了Execute用法的同时对注入逻辑进行了简化
- aspectd还需要aspect_impl等,本项目可以直接在主程序代码中添加注入代码,也可以用plugin的方式添加
- 本项目不需要引入任何第三方包,用pragma注解完成对应插桩
- 支持hot restart,aspectd的注入代码修改后,需要重启程序才能看到改变,这样很不利于差桩代码的编写,aspect_frontend_server的差桩代码在改变后可以直接通过hot restart就能看到最新的改变。
在维护aspect_frontend_server过程中也碰到了一些bug,感谢反馈bug的小伙伴。目前已知的问题已经修复:
- extionsion和mixin方法注入
- 实现try-catch的注入
- 实现getter方法的注入
- 解决函数返回值为Futnre 范型报错的问题
- 解决编译添加--obfuscate混淆出错的问题
- 解决isRegex参数某些情况不起作用的bug等等
希望有更多的小伙伴使用并反馈问题,我会一直将这个项目维护下去。