Flutter/Dart:如何在Dart FFI中使用异步回调?

7

我的应用程序后端是用C ++编写的,前端是使用Dart / flutter编写的。 我希望在数据准备就绪时,后端通知前端。 这需要在Dart和C ++之间实现异步回调机制。

环境

$ flutter doctor -v
[✓] Flutter (Channel stable, 1.20.1, on Mac OS X 10.15.5 19F101, locale
    en-CN)
    • Flutter version 1.20.1 at /Applications/Android/flutter
    • Framework revision 2ae34518b8 (2 days ago), 2020-08-05 19:53:19 -0700
    • Engine revision c8e3b94853
    • Dart version 2.9.0
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

 
[✓] Android toolchain - develop for Android devices (Android SDK version
    30.0.1)
    • Android SDK at /Applications/Android/sdk
    • Platform android-30, build-tools 30.0.1
    • ANDROID_HOME = /Applications/Android/sdk
    • ANDROID_SDK_ROOT = /Applications/Android/sdk
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.6, Build version 11E708
    • CocoaPods version 1.8.4

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 48.0.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6222593)

[✓] VS Code (version 1.47.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.8.1

 
[!] Connected device                          
    ! No devices available

我所做的事情

我查看了Dart团队在GitHub上的示例

为了将示例代码适配到一个Flutter项目中,我进行了如下操作:

  • 创建了一个FFI插件项目
  • 用示例函数替换main函数
  • 删除了一些不必要的依赖关系,例如expect
  • 使用引用的C++代码创建本地库,例如dart-sdk-master/runtime/bin/ffi_test/ffi_test_functions.cc

问题

然而,我无法运行这个示例程序。

Dart版本

我的第一个问题是直接运行示例时Dart版本不受支持。我当前使用的是最新的稳定渠道版本,也尝试了开发渠道版本。报错信息如下:

Error: The specified language version is too high. The highest supported language version is 2.9.

本地编译

这个例子中缺少有关本地端实现的信息,因此我遇到了许多编译器错误。

Launching lib/main.dart on ONEPLUS A6000 in debug mode...
Running Gradle task 'assembleDebug'...

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:externalNativeBuildDebug'.
> Build command failed.
  Error while executing process /Applications/Android/sdk/cmake/3.6.4111459/bin/cmake with arguments {--build /path/to/ffiasync/example/android/app/.cxx/cmake/debug/armeabi-v7a --target ffiasync}
  [1/2] Building CXX object CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc.o
  [2/2] Linking CXX shared library /path/to/ffiasync/example/build/app/intermediates/cmake/debug/obj/armeabi-v7a/libffiasync.so
  FAILED: : && /Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++  --target=armv7-none-linux-androideabi16 --gcc-toolchain=/Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security   -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libffiasync.so -o /path/to/ffiasync/example/build/app/intermediates/cmake/debug/obj/armeabi-v7a/libffiasync.so CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffiasync.cpp.o CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc.o  -latomic -lm && :
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:54: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:60: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:66: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:74: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:143: error: undefined reference to 'Dart_CurrentIsolate'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:183: error: undefined reference to 'ClobberAndCall'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:277: error: undefined reference to 'Dart_InitializeApiDL(void*)'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:288: error: undefined reference to 'Dart_DumpNativeStackTrace'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:289: error: undefined reference to 'Dart_PrepareToAbort'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewNativePort_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_CloseNativePort_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:791: error: undefined reference to 'Dart_NewPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:793: error: undefined reference to 'Dart_HandleFromPersistent'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:794: error: undefined reference to 'Dart_DeletePersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:795: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:796: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:800: error: undefined reference to 'Dart_IsNull'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:802: error: undefined reference to 'Dart_NewWeakPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:804: error: undefined reference to 'Dart_HandleFromWeakPersistent'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:809: error: undefined reference to 'Dart_DeleteWeakPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:826: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:829: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:846: error: undefined reference to 'Dart_NewStringFromCString'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:847: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:848: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:850: error: undefined reference to 'Dart_NewInteger'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:851: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:852: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:855: error: undefined reference to 'Dart_Invoke'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:874: error: undefined reference to 'Dart_NewStringFromCString'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:879: error: undefined reference to 'Dart_GetField'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:885: error: undefined reference to 'Dart_IntegerToInt64'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:895: error: undefined reference to 'Dart_EnterScope'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:900: error: undefined reference to 'Dart_ExitScope'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:905: error: undefined reference to 'Dart_True'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_SetPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeletePersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewWeakPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromWeakPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeleteWeakPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeletePersistentHandle_DL'
  clang++: error: linker command failed with exit code 1 (use -v to see invocation)
  ninja: build stopped: subcommand failed.
  


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 12s
Exception: Gradle task assembleDebug failed with exit code 1

问题

由于无法得到 Dart 团队的答复,我有一些问题:

  • 我是否需要从源代码构建 Dart 运行时才能使异步回调工作?上面的错误似乎表明我缺少运行时支持。这是否意味着 FFI 异步回调未包含在官方的 Dart/Flutter 发布版中?
  • 是否有可以直接使用的 FFI 异步回调示例或文档?

恐怕凭借这点信息,靠尝试和错误可能难以解决问题。


C++ 和 Dart 之间的套接字怎么样? - Yadu
你找到这个问题的答案了吗? - Illya S
@Yadu 感谢提醒。我也意识到,由于 WebSocket 已经内置于 Dart 中,因此它可能是 Dart-C++ 通信的更简单的解决方案。我正在钻研。 - kakyo
是的,我猜对了,最好在您的应用程序的cpp和dart代码之间使用服务器和客户端架构,即使Flutter插件架构也是如此(大部分是猜测)。 - Yadu
@Yadu 我同意。目前来看,这对我来说不太可行,因为延迟是一个问题,这就是为什么我坚持采用进程内方法的原因。 - kakyo
显示剩余3条评论
2个回答

6

也许看一下我的新存储库会有所帮助 - 我成功地让一个JUCE(C ++)后端运行自己的线程,可以调用Flutter UI: JucyFluttering


你的代码库看起来是一个很好的参考。我会在有时间的时候尝试你的建议,并在让它工作时将你的答案标记为已接受。谢谢! - kakyo

5

同意,不清楚在构建Android共享库或iOS代码时如何链接Dart SDK可执行文件(?)或共享库(?)。 它编译了,但无法链接。

鉴于Dart是单线程的,而异步回调技术涉及向主Dart线程发信号以指示现在是时候调用C以允许在该单个主线程上进行回调,我从未看到比从Dart轮询(可能来自定时器)以查看响应是否准备好更有优势。 在60fps下,知道响应在16ms间隔的一半处已准备好有多大意义呢? 更改仍将在下一个绘图之前反映出来。 当然,轮询不太高效,但每16ms一次并不那么昂贵。


1
另一个老套路是使用套接字作为回环。在Dart端打开一个服务器套接字,将套接字号码传递给C代码。C代码连接到套接字。每当C想要向Dart发出信号时,它会写入套接字,而Dart会接收到一个事件。这就像通过操作系统进行进程内通信。 - Richard Heap
谢谢。60帧每秒的点很有道理。我最终选择了轮询,到目前为止还没有遇到主要的性能问题。 - kakyo
那么您的意思是我们可以这样做吗? Dart -> C函数 -> 启动新线程 -> 从C函数返回 -> 返回到Dart。 然后定期从Dart调用另一个C函数以检查线程是否已完成并准备好结果。即使我们退出并返回到Dart线程,从C函数启动的新线程是否会继续运行? - Illya S
3
@RichardHeap 是的,这确实有效。我甚至不需要第二次调用C代码 - 只需一次启动本地线程,然后您可以从Dart代码监视本地线程的完成情况 - 为此,您只需将指向变量(标志)的指针从Dart代码传递到C代码,然后从Dart代码监视此变量。 C代码需要在本地线程完成后设置此标志 - 通知Dart代码完成。 - Illya S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接