问题
My app's backend is written in C++ and the frontend in Dart/flutter. I'd love to have the backend notify frontend whenever data is ready. This requires implementing an async callback scheme between Dart and C++.
Environment
$ 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
What I did
I looked at the github example from the Dart team.
To adapt the sample code to a flutter project, I did the following
- Create a FFI plugin project
- Replace the
main
function with the sample function - Remove unimportant dependencies such as
expect
- Create native library using the referenced C++ code such as
dart-sdk-master/runtime/bin/ffi_test/ffi_test_functions.cc
Problems
However, I couldn't get the sample to run.
Dart version
The first problem I had was that the dart version was not supported when I run the sample directly. I'm on latest stable channel, and also tried dev channel. The error says
Error: The specified language version is too high. The highest supported language version is 2.9.
Native compilation
The information on the native-side implementation is missing in the example. So I'm left with lots of compiler errors.
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
Questions
Unable to get answers from Dart team, I have a few questions
- Should I build Dart runtime from source to make the async callback work? The errors above seems to say I'm missing runtime support. Does that mean FFI async callback is not included in the official dart/flutter releases?
- Is there a ready-to-run ffi async callback sample or documentation somewhere?
I'm afraid that with this little info, it would be too difficult to figure it out by trail-and-error.
回答1:
Agreed, it's not clear how you link against the Dart SDK executable(?)/shared library(?) while building your Android shared library or your iOS code. It compiles, but won't link.
Given that Dart is single-threaded and the async callback technique involves signalling the main Dart thread to indicate that it's time to call down to C to allow a callback to take place on that single, main thread, I've never seen much advantage over, say, polling from Dart (maybe from a timer) to see whether the response is ready. At 60fps, is there much point knowing that the response is ready half way through a 16ms inter-frame interval? The change isn't going to reflect until the next paint anyway. Sure, it doesn't feel very efficient to poll, but once every 16ms isn't that expensive.
回答2:
It might help to look at my new repository - I managed to have a JUCE (C++) Backend running its own thread that can call Flutter UI: JucyFluttering
来源:https://stackoverflow.com/questions/63311092/flutter-dart-how-to-use-async-callback-with-dart-ffi