颤振/飞镖:如何在飞镖 FFI 中使用异步回调?

Posted

技术标签:

【中文标题】颤振/飞镖:如何在飞镖 FFI 中使用异步回调?【英文标题】:flutter/dart: How to use async callback with Dart FFI? 【发布时间】:2020-11-28 08:09:37 【问题描述】:

我的应用程序的后端是用 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

我做了什么

我看了the github example from the Dart team。

为了使示例代码适应颤振项目,我做了以下操作

创建 FFI 插件项目 用示例函数替换 main 函数 删除不重要的依赖,例如expect 使用引用的 C++ 代码(例如 dart-sdk-master/runtime/bin/ffi_test/ffi_test_functions.cc)创建本机库

问题

但是,我无法运行示例。

飞镖版

我遇到的第一个问题是直接运行示例时不支持 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 运行时以使异步回调工作吗?上面的错误似乎表明我缺少运行时支持。这是否意味着官方 dart/flutter 版本中不包含 FFI 异步回调? 是否有可以运行的 ffi 异步回调示例或文档?

恐怕只有这么少的信息,很难通过跟踪和错误来弄清楚。

【问题讨论】:

c++ 和 dart 之间的套接字怎么样? 你找到这个问题的答案了吗? @Yadu 谢谢提醒。我还意识到 websocket 可能是 Dart-C++ comm 更简单的解决方案,因为它内置在 Dart 中。我正在调查。 @ElyaS 不幸的是,没有。但是您可以在下面查看达曼的答案。我没有时间检查,但值得一看。如果它适合您,请随时告诉我们。 有一件事我不明白,请澄清一下,如果后端是 cpp,那么这与 dart ffi 有什么关系?我想后端是服务器???? 【参考方案1】:

查看我的新存储库可能会有所帮助 - 我设法让 JUCE (C++) 后端运行自己的线程,该线程可以调用 Flutter UI: JucyFluttering

【讨论】:

您的 repo 似乎是一个很好的参考。当我有时间时,我会尝试你的提示,并在我成功后将你的答案标记为已接受。谢谢!【参考方案2】:

同意,目前尚不清楚在构建 Android 共享库或 iOS 代码时如何链接到 Dart SDK 可执行文件(?)/共享库(?)。它可以编译,但不会链接。

鉴于 Dart 是单线程的,并且异步回调技术涉及向 Dart 主线程发出信号以指示是时候调用 C 以允许在该单个主线程上发生回调,我从未见过比从 Dart 轮询(可能来自计时器)来查看响应是否准备好有很大的优势。在 60 fps 时,知道响应在 16 毫秒的帧间间隔中途准备就绪有什么意义?无论如何,这种变化要等到下一次油漆才会体现出来。当然,轮询的感觉不是很有效,但每 16 毫秒一次并没有那么昂贵。

【讨论】:

另一个老技巧是使用套接字作为环回。在 Dart 端打开一个服务器套接字,将套接字号传递给 C 代码。 C 代码连接到套接字。每当 C 想要向 Dart 发送信号时,它都会写入套接字,并且 Dart 会收到一个事件。这就像通过 O/S 进行进程内通信。 谢谢。 60fps 点是有道理的。我最终进行了投票,到目前为止还没有看到那里出现重大的性能问题。 那么您的意思是我们可以执行以下操作吗? Dart -> C-function -> 启动新线程 -> 从 C-function 返回 -> 返回 Dart。然后定期从 Dart 调用另一个 C 函数来检查线程是否完成并且结果是否准备好。即使我们退出并返回 Dart 线程,从 C 函数启动的新线程是否会继续? @RichardHeap 是的,这确实有效。我什至不需要第二次调用 C 代码 - 只需一次启动本机线程,然后您可以从 Dart 代码监视本机线程的完成情况 - 因为您可以简单地将指针传递给来自的变量(标志)将 Dart 代码转换为 C 代码,然后从 Dart 代码中监视此变量。一旦本地线程完成,C 代码需要设置这个标志 - 通知 Dart 代码完成。

以上是关于颤振/飞镖:如何在飞镖 FFI 中使用异步回调?的主要内容,如果未能解决你的问题,请参考以下文章

你如何在颤振飞镖中渲染脚本标签

如何在颤振/飞镖的多级下拉菜单中使用此 Json

如何取消颤振/飞镖集团中的“等待”

如何在颤振飞镖中使用相同的元素初始化列表?

如何在颤振/飞镖中捕获平台异常?

如何在飞镖/颤振中设置或删除地图标记