Beike_AspectD

这是 AspectD 的一个分支。

Beike_AspectD 是一个用于 Dart 的 AOP 框架。AspectD 提供了 call/execute/inject 语法供开发者操作 Dart 代码。除此之外,Beike_AspectD 还提供了:

  • ✅ 支持添加语法为类添加方法。
  • ✅ 支持字段获取语法来拦截字段的读取操作。
  • ✅ 支持 Flutter Web。

我们可以使用 Beike_AspectD 做什么?

Beike 在许多包中都使用了 Beike_AspectD。

  • 事件跟踪。
  • JSON 转 Model。
  • 性能监控。
  • Flutter 框架 Bug 修复。

安装

1. 应用 flutter_tools.patch。

cd path-for-flutter-tools-git-repo
git apply --3way path-for-beike_aspectd-package/inner/0001-aspectd.patch
rm ../../bin/cache/flutter_tools.stamp

下次构建项目时,flutter tools 会自动构建。

2. 将 Beike_AspectD 添加到你的 yaml 文件。

dependencies:
  beike_aspectd:
    path: ../

3. 将 aop_config.yaml 添加到你的 Flutter 项目。

在 Flutter 项目的根目录(与 pubspec.yaml 同级)下添加一个名为 aop_config.yaml 的文件。

你可以从 Beike_AspectD 的示例中复制该文件。
文件的内容如下:

flutter_tools_hook:
  - project_name: 'beike_aspectd'
    exec_path: 'bin/starter.snapshot'

Flutter_tools 会检查该文件以确定 Beike_AspectD 是否已启用。然后它会获取 starter.snapshot 来处理 dill 文件。

4. 编写你的 hook 文件并导入该文件。

hook_example.dart (AOP 实现)

import 'package:beike_aspectd/aspectd.dart';

@Aspect()
@pragma("vm:entry-point")
class CallDemo {
  @pragma("vm:entry-point")
  CallDemo();

 //实例方法
 @Call("package:example/main.dart", "_MyHomePageState",
     "-_incrementCounter")
 @pragma("vm:entry-point")
 void _incrementCounter4(PointCut pointcut) {
   print('call instance method2!');
   pointcut.proceed();
 }
}

由于 hook_example.dart 没有在你的项目中被使用,我们应该在项目中导入它,否则它会在编译时被 tree shaking 掉。

例如,我们可以在 main.dart 中导入该文件。

// ignore: unused_import
import 'package:example/hook_example.dart';

教程

除了 AspectD 提供的 3 种 AOP 编程方式(call/execute/inject)外,Beike_AspectD 还提供了 add/field get 操作。

add

向类添加方法,支持类名正则匹配,支持父类过滤。

  @Add("package:.+\\.dart", ".*", isRegex: true)
  @pragma("vm:entry-point")
  dynamic getBasicInfo(PointCut pointCut) {
    return pointCut?.sourceInfos ?? {};
  }

上面的代码将 getBasicInfo() 方法添加到所有类中,你可以使用自己的正则表达式和 superCls 参数来过滤类。
我们可以使用以下代码调用该函数。

    dynamic self = someinstance;
    Map info = self.getBasicInfo(PointCut.pointCut());

field get

字段的每一个调用点都会被操作。

 @pragma("vm:entry-point")
 @FieldGet('package:example/main.dart', 'MyApp', 'field', false)
 static String exchange2(PointCut pointCut) {
    return 'Beike_Aspectd';
}

假设 MyApp 类有一个名为 field 的属性,通过使用上面的代码,当调用属性 field 时,它总是返回字符串 ‘Beike_Aspectd’。

兼容性

目前 Beike_Aspectd 支持 flutter 1.22.4 和 2.2.2。

问答

  • 如何知道我的代码是否被成功 hook 了?
    1. 首先,你需要下载 dart-sdk 并检出到 flutter 的相应 revision。dart 的 revision 可以在 path_to_flutter/bin/cache/dart-sdk/revision 找到。
    2. 运行以下命令。

    path_to_flutter/bin/cache/dart-sdk/bin/dart  path_to_dart/pkg/vm/bin/dump_kernel.dart path_to_your_project/.dart_tool/flutter_build/***/app.dill output_path/out.dill.txt
    1. 打开 output_path/out.dill.txt 文件,检查你的代码是否被 hook 了。

联系方式

如果您有任何问题,请随时提交 issue。

GitHub

查看 Github