使用 dart:ffi 调用原生代码

Flutter mobile can use the dart:ffi libraryto call native C APIs. FFI stands forforeign function interface.Other terms for similar functionality includenative interface and language bindings.

Before your library or program can use the FFI libraryto bind to native code, you must ensure that thenative code is loaded and its symbols are visible to Dart.This page focuses on compiling, packaging,and loading native code within a Flutter plugin or app.

This tutorial demonstrates how to bundle C/C++sources in a Flutter plugin and bind to them usingthe Dart FFI library on both Android and iOS.In this walkthrough, you’ll create a C functionthat implements 32-bit addition and thenexposes it through a Dart plugin named “native_add”.

备忘 The dart:ffi library is in beta, and breaking API changes might still happen.

Using the feature requires a Flutter 1.10.x dev channel build. To switch to the dev channel and upload the latest dev version, do the following:

  1. $ flutter channel dev
  2. $ flutter upgrade

For more information on Flutter’s channels, see Upgrading Flutter.

Dynamic vs static linking

A native library can be linked into an app eitherdynamically or statically. A statically linked libraryis embedded into the app’s executable image,and is loaded when the app starts.

Symbols from a statically linked library can beloaded using DynamicLibrary.executable orDynamicLibrary.process.

A dynamically linked library, by contrast, is distributedin a separate file or folder within the app,and loaded on-demand. On Android, a dynamicallylinked library is distributed as a set of .so (ELF)files, one for each architecture. On iOS,it’s distributed as a .framework folder.

A dynamically linked library can be loaded intoDart via DynamicLibrary.open.

API documentation is available from the Dart dev channel:Dart API reference documentation.

Step 1: Create a plugin

If you already have a plugin, skip this step.

To create a plugin called “native_add”,do the following:

  1. $ flutter create --template=plugin native_add
  2. $ cd native_add

Step 2: Add C/C++ sources

You need to inform both the Android and iOS buildsystems about the native code so the code can be compiledand linked appropriately into the final application.

You add the sources to the ios folder,because CocoaPods doesn’t allow including sourcesabove the podspec file, but Gradle allows you to pointto the ios folder. It’s not required to use the samesources for both iOS and Android;you may, of course, add Android-specific sourcesto the android folder and modify CMakeLists.txtappropriately.

The FFI library can only bind against C symbols,so in C++ these symbols must be marked extern C.You should also add attributes to indicate that thesymbols are referenced from Dart,to prevent the linker from discarding the symbolsduring link-time optimization.

For example,to create a C++ file named ios/Classes/native_add.cpp,use the following instructions. (Note that the templatehas already created this file for you.) Start from theroot directory of your project:

  1. cat > ios/Classes/native_add.cpp << EOF
  2. #include <stdint.h>
  3. extern "C" __attribute__((visibility("default"))) __attribute__((used))
  4. int32_t native_add(int32_t x, int32_t y) {
  5. return x + y;
  6. }
  7. EOF

On iOS, you need to tell xcode to statically link the file:

  • In Xcode, open Runner.xcodeproj.
  • Add the C/C++/Objective-C/Swiftsource files to the Xcode project.On Android, you need to create a CMakeLists.txt fileto define how the sources should be compiled and pointGradle to it. From the root of your project directory,use the following instructions
  1. cat > android/CMakeLists.txt << EOF
  2. cmake_minimum_required(VERSION 3.4.1) # for example
  3. add_library( native_add
  4. # Sets the library as a shared library.
  5. SHARED
  6. # Provides a relative path to your source file(s).
  7. ../ios/Classes/native_add.cpp )
  8. EOF

Finally, add an externalNativeBuild section toandroid/build.gradle. For example:

  1. android {
  2. // ...
  3. externalNativeBuild {
  4. // Encapsulates your CMake build configurations.
  5. cmake {
  6. // Provides a relative path to your CMake build script.
  7. path "CMakeLists.txt"
  8. }
  9. }
  10. // ...
  11. }

Step 3: Load the code using the FFI library

In this example, you can add the following code tolib/native_add.dart. However the location of theDart binding code is not important.

First, you must create a DynamicLibrary handle tothe native code. This step varies between iOS and Android:

  1. import 'dart:ffi'; // For FFI
  2. import 'dart:io'; // For Platform.isX
  3. final DynamicLibrary nativeAddLib =
  4. Platform.isAndroid
  5. ? DynamicLibrary.open("libnative_add.so")
  6. : DynamicLibrary.process();

Note that on Android the native library is namedin CMakeLists.txt (see above),but on iOS it takes the plugin’s name.

With a handle to the enclosing library,you can resolve the native_add symbol:

  1. final int Function(int x, int y) nativeAdd =
  2. nativeAddLib
  3. .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add")
  4. .asFunction();

Finally, you can call it. To demonstrate this withinthe auto-generated “example” app (example/lib/main.dart):

  1. // Inside of _MyAppState.build:
  2. body: Center(
  3. child: Text('1 + 2 == ${nativeAdd(1, 2)}'),
  4. ),

Other use cases

iOS and macOS

Dynamically linked libraries are automatically loaded bythe dynamic linker when the app starts. Their constituentsymbols can be resolved using DynamicLibrary.process.You can also get a handle to the library withDynamicLibrary.open to restrict the scope ofsymbol resolution, but it’s unclear how Apple’sreview process handles this.

Symbols statically linked into the application binarycan be resolved using DynamicLibrary.executable orDynamicLibrary.process.

Platform library

To link against a platform library,use the following instructions:

  • In Xcode, open Runner.xcodeproj.
  • Select the target platform.
  • Click + in the Linked Frameworks and Librariessection.
  • Select the system library to link against.

First-party library

A first-party native library can be included eitheras source or as a (signed) .framework file.It’s probably possible to include statically linkedarchives as well, but it requires testing.

Source code

To link directly to source code,use the following instructions:

  • In Xcode, open Runner.xcodeproj.
  • Add the C/C++/Objective-C/Swiftsource files to the Xcode project.
  • Add the following prefix to theexported symbol declarations to ensure theyare visible to Dart:

C/C++/Objective-C

  1. extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used))

Swift

  1. @_cdecl("myFunctionName")

Compiled (dynamic) library

To link to a compiled dynamic library,use the following instructions:

  • If a properly signed Framework file is present,open Runner.xcodeproj.
  • Add the framework file to the Embedded Binariessection.
  • Also add it to the Linked Frameworks & Librariessection of the target in Xcode.

Open-source third-party library

To create a Flutter plugin that includes bothC/C++/Objective-C and Dart code,use the following instructions:

  • In your plugin project,open ios/<myproject>.podspec.
  • Add the native code to the source_filesfield.The native code is then statically linked intothe application binary of any app that usesthis plugin.

Closed-source third-party library

To create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:

  • In your plugin project,open ios/<myproject>.podspec.
  • Add a vendored_frameworks field.See the CocoaPods example.Do not upload this plugin(or any plugin containing binary code)to Pub. Instead, this plugin should be downloadedfrom a trusted third-party,as shown in the CocoaPods example.

Android

Platform library

To link against a platform library,use the following instructions:

  1. DynamicLibrary.open('libGLES_v3.so');

You might need to update the Android manifestfile of the app or plugin if indicated bythe documentation.

First-party library

The process for including native code in sourcecode or binary form is the same for an app orplugin.

Open-source third-party

Follow the Add C and C++ code to your projectinstructions in the Android docs toadd native code and support for the nativecode toolchain (either CMake or ndk-build).

Closed-source third-party library

To create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:

  • Open the android/build.gradle file for yourproject.
  • Add the AAR artifact as a dependency.Don’t include the artifact in yourFlutter package. Instead, it should bedownloaded from a repository, such asJCenter.

Web

Plugins are not yet supported for web apps.