How to develop FFI type plug-ins in the flutter project

How to develop FFI type plug-ins in the flutter project

foreword

In the previous article, we discussed how to use the ffi library officially provided by flutter to bind the C source code in different platform directories, that is, to generate a plugin type project, and then specify the platform directory in the project, according to The compilation methods of different platforms generate static or dynamic link libraries, and finally use the dart code to load the link library, and then convert the local method symbols into dart methods.
However, this is the way used by the old version. After the flutter3.0 version, the official provides a new way to integrate the C source code function. This time we will take a look at this new method - FFI plugin.

FFI plugin

insert image description here
The above picture is a brief description of the FFI plugin in the official document. It can be seen that the FFI plugin is specially designed for binding local source code. Although the conventional plugin can also support it, the main purpose is to support the method channel, that is, the dart call APIs of various related platforms (Java or Kotlin API in Android, Objective-C or Swift API in iOS, C++ API in Windows operating system), and the official meaning is support for C source code functions after 3.0 ffi plugin It will be more powerful, so if we just call C code and don't need platform SDK API, we can consider using FFI plugin.

Integration steps

  1. As before, we first create the project using the command line:
flutter create --platforms=android,ios --template=plugin_ffi hello
  1. Add C/C++ source code and related compilation configuration files:
    After the creation is complete, let’s observe the directory structure of the FFI plugin project. Compared with conventional plugins, there are mainly the following differences: the
    local source code files and CmakeFile.txt files are now put together Under the src directory of the project, the source files under the ios platform directory Classes exist, but the source code under src is introduced. The cmake path in the externalNativeBuild attribute in the android platform build.gradle file also points to CmakeFile.txt in src.
// Relative import to be able to reuse the C sources.information.
#include "../../src/hello.c"
android {
    
    
    externalNativeBuild {
    
    
        cmake {
    
    
            path "../src/CMakeLists.txt"
        }
    }
}
  1. Source code compilation and binding
    The pubspec.yaml in the project provides the following configuration options:
  plugin:
    platforms:
      android:
        ffiPlugin: true
      ios:
        ffiPlugin: true

It means to use ffiPlugin to compile the source code for different platforms, and bind the binary files to integrate into the flutter application. Which platforms you need need to be reflected in this configuration item.

  1. Loading library and converting to dart method
    The ffiPlugin project provides us with a way to use the source code to automatically generate a dart method according to certain conversion rules. This is done through the ffigen.yaml file and the ffigen command:
flutter pub run ffigen --config ffigen.yaml

The content of ffigen.yaml is as follows:

# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: HelloBindings
description: |
  Bindings for `src/hello.h`.

  Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/hello_bindings_generated.dart'
headers:
  entry-points:
    - 'src/hello.h'
  include-directives:
    - 'src/hello.h'
preamble: |
  // ignore_for_file: always_specify_types
  // ignore_for_file: camel_case_types
  // ignore_for_file: non_constant_identifier_names
comments:
  style: any
  length: full

src/hello.h is as follows:

...
#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif
FFI_PLUGIN_EXPORT intptr_t sum(intptr_t a, intptr_t b);
FFI_PLUGIN_EXPORT intptr_t sum_long_running(intptr_t a, intptr_t b);
...
import 'dart:ffi' as ffi;
class HelloBindings {
    
    
  /// Holds the symbol lookup function.
  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
      _lookup;
  /// The symbols are looked up in [dynamicLibrary].
  HelloBindings(ffi.DynamicLibrary dynamicLibrary)
      : _lookup = dynamicLibrary.lookup;

   int sum(
    int a,
    int b,
  ) {
    
    
    return _sum(
      a,
      b,
    );
  }

  late final _sumPtr =
      _lookup<ffi.NativeFunction<ffi.IntPtr Function(ffi.IntPtr, ffi.IntPtr)>>(
          'sum');
  late final _sum = _sumPtr.asFunction<int Function(int, int)>();
  ...
 }

It can be seen that it automatically generates the dart code according to the local method defined in the header file. There is a HelloBindings class in this code file, and there is a mapping relationship between the methods in it and the methods in the header file.

  1. The calling method
    lib/hello.dart file is as follows:
const String _libName = 'hello';

/// The dynamic library in which the symbols for [HelloBindings] can be found.
final DynamicLibrary _dylib = () {
    
    
  if (Platform.isMacOS || Platform.isIOS) {
    
    
    return DynamicLibrary.open('$_libName.framework/$_libName');
  }
  if (Platform.isAndroid || Platform.isLinux) {
    
    
    return DynamicLibrary.open('lib$_libName.so');
  }
  if (Platform.isWindows) {
    
    
    return DynamicLibrary.open('$_libName.dll');
  }
  throw UnsupportedError('Unknown platform: ${
      
      Platform.operatingSystem}');
}();

/// The bindings to the native functions in [_dylib].
final HelloBindings _bindings = HelloBindings(_dylib);
int sum(int a, int b) => _bindings.sum(a, b);

In this file, we still need to load the local library file through DynamicLibrary, and then pass the instance to the construction method of the class. When the sum method is called, the specific conversion details are implemented in the HelloBindings class.

Guess you like

Origin blog.csdn.net/Yaoobs/article/details/129216628