2021年3月份,官方宣布发布 Flutter 2.0 正式版,至此,FFI 终于不再是 beta 版本,而是正式稳定版。
  FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi 库来调用 C 语言编写的代码。
 
  在某些开发场景下,使用 FFI 比使用 Channel 要好很多,FFI 不需要 await / async ,代码更自然、更舒适。
 
  官方 FFI 使用示例:https://flutter.cn/docs/development/platform-integration/c-interop
 
  需要注意一点:dart:ffi 库只能用来调用 C 语言 API,不支持调用 C++ 语言 API,因此,C++的代码需要包装成 C 风格才行。
 
  下面通过一个示例来演示 FFI 的基本使用步骤。
 

1、创建 Flutter 工程

  创建一个名称为 “native_add” 的工程,含 android 和 ios 平台:

flutter create --platforms=android,ios native_add
cd native_add

 

2、定义 C API

  新建 native_add.cpp 文件,位于 ios/Runner

cat ios/Runner/native_add.cpp 
#include <stdint.h>

extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
    return x + y;
}

  可以将 C 代码放到 ios/Runner 目录下,因为 CocoaPods 不允许代码处于比 podspec 文件更高的目录层级,但是 Gradle 允许您指向 ios 文件夹。iOS 和 Android 可以共用一套C代码;当然,也可以将特定于 Android 的代码添加到 android 文件夹并修改 CMakeLists.txt 文件。
 
 dart:ffi 只允许和 C 符号进行绑定,因此如果是 C++ 编写的符号,需要添加 extern C 标记。另外,还应该添加属性来表明符号是被 Dart 引用的,这样可以防止链接器在优化链接时会丢弃符号,这里说的属性就是上面的 __attribute__((visibility("default"))) __attribute__((used))
 

3、不同平台的工程配置

3.1 iOS 平台配置

  在 iOS 中,您需要告诉 Xcode 如何静态链接这个文件,步骤如下:

  1. 使用 Xcode 打开 ios/Runner.xcworkspace
  2. 右键点击【Runner】目录,然后点击【Add Files to “Runner” …】;
  3. 添加 native_add.cpp 源码文件到 Xcode 工程中,如下图所示;

flutter 运行python flutter调用jni_flutter 运行python

flutter 运行python flutter调用jni_flutter_02



3.2 Android 平台配置

  在 Android 中,需要在创建一个 CMakeLists.txt 文件用来定义如何编译源文件,同时告诉 Gradle 如何去定位它们。
 首先,在 android/app 目录中,创建 CMakeLists.txt 文件,内容如下:

cmake_minimum_required(VERSION 3.4.1)  # for example

add_library( native_add

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ../../ios/Runner/native_add.cpp )

  然后,打开 android/app/build.gradle 文件,添加以下内容:

android {
	// ...
    externalNativeBuild {
        // Encapsulates your CMake build configurations.
        cmake {
          // Provides a relative path to your CMake build script.
          path "CMakeLists.txt"
        }
    }
    // ...
}

  最后,修改 android/app/local.properties 文件,添加一行代码 ndk.dir,如下:

sdk.dir=/Users/clarkwain/Developer/android_sdk
ndk.dir=/Users/clarkwain/Developer/android-ndk-r21e
flutter.sdk=/Users/clarkwain/Developer/flutter

  说明:

  • ndk.dir 表示指定NDK所在目录
     

4、Dart 中调用 C API

  上面完成各个平台的配置之后,接着就可以在 Dart 中编写调用 C 的函数了。

  首先,打开 lib/main.dart文件,修改为:

import 'package:flutter/material.dart';
import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX

void main() {
  runApp(FFIDemo());
}

class FFIDemo extends StatefulWidget {
  const FFIDemo({Key key}) : super(key: key);

  @override
  _FFIDemoState createState() => _FFIDemoState();
}

class _FFIDemoState extends State<FFIDemo> {
  DynamicLibrary nativeAddLib;
  int Function(int x, int y) nativeAddFunc;

  @override
  void initState() {
    super.initState();

    //加载 C 符号
    nativeAddLib = Platform.isAndroid
        ? DynamicLibrary.open("libnative_add.so")
        : DynamicLibrary.process();

    //查找"native_add"符号,并转为 Dart 函数
    nativeAddFunc = nativeAddLib
        .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add")
        .asFunction();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Center(
        child: Text(
          "1+1=${nativeAddFunc(1, 1)}", //调用 C 的 native_add() 函数
        ),
      ),
    );
  }
}

 

5、运行效果如下:

  代码编写完之后,即可以运行看效果:

  

flutter 运行python flutter调用jni_flutter 运行python_03


    

 

6.总结

  
  上面就是 Flutter FFI 的使用示例了,在后面的章节中,将会详细介绍 FFI 的使用,欢迎关注。