flutter项目中C语言代码的集成与调用

flutter项目中C语言代码的集成与调用

前言

学习flutter开发也已经有一段日子了,不过大部分时间还是花费在研究如何更好地绘制界面以及与用户交互,对底层技术的探索不够深入。其实flutter官方的SDK给开发者提供了很多功能强大的库,帮助开发者更好地发掘flutter应用的潜力,而不只是停留在表面。刚好最近有一个项目,需要用flutter开发出一款可以利用蓝牙,wifi,NFC与智能硬件进行交互的应用,虽然github上已经有一些别人封装好的第三方库可以使用,但是为了能够最大化地定制所需的功能,我们决定尝试一下直接集成C库,这个也是一个学习的过程。

是否支持

通过查阅flutter官方文档,我们惊喜地发现flutter官方提供了一个dart:ffi(Foreign Function Interface: 外部功能接口)库来调用本地的 C API。下面我们就一起来看看如何将C代码集成到flutter项目中并且调用其中的方法。
注:目前此功能明确只支持ios,android与macos,不支持web,是否支持windows有待研究。。

集成方法

官方推荐的集成方法是生成一个flutter的plugin,然后在这个plugin里对应平台(ios/android)目录下添加C源代码,并根据平台指定的方式进行编译并链接到最终的程序中。下面我们一步一步来看:

  1. 生成plugin,利用命令flutter create --platforms=android,ios --template=plugin native_add,其中platplatforms是支持的平台,每个平台会生成对应的目录和相关配置文件等,template是指创建flutter项目的类型,这里我们选择plugin即插件的形式。
  2. 添加C/C++源码,作为示例,我们在ios目录下的Classes路径下添加一个native_add.cpp文件(CocoaPods 不允许源码处于比 podspec 文件更高的目录层级,但是 Gradle 允许你指向 ios 文件夹,所以我们偏向于将源代码放到ios目录下)
    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;
}

一个实现 32 位的加法 C 函数
备注:

这句话是官方文档里的,没有完全理解
针对android平台的话,你需要创建一个 CMakeLists.txt 文件用来定义如何编译源文件,同时告诉 Gradle 如何去定位它们。

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/Classes/native_add.cpp )

最后,添加一个 externalNativeBuild 到你的 android/build.gradle 文件中。示例如下:

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

这样最终会在android平台编辑生成一个动态链接库.so文件

  1. 使用 FFI 库绑定本地代码
    接下来,我们需要在 lib/native_add.dart文件中编写一些代码,将本地源代码转换成Dart代码
    首先,你需要创建一个 DynamicLibrary 来处理本地代码。这一步在 iOS 和 Android 之间有所不同:
import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX

final DynamicLibrary nativeAddLib = Platform.isAndroid
    ? DynamicLibrary.open('libnative_add.so') 
    : DynamicLibrary.process();
        //静态链接中的符号可以使用 DynamicLibrary.executable 
    	//或 DynamicLibrary.process 来加载。
		//动态链接库在 Dart 中可以通过 DynamicLibrary.open 加载。

在 Android 上,库的名称是定义在 CMakeLists.txt 中的(见上文),但在 iOS 上,它将使用插件的名称(flutter create 命令最后的名字)。
接着,我们通过使用库的句柄来解析native_add 符号,将本地方法转化为dart可以使用的方法

final int Function(int x, int y) nativeAdd = nativeAddLib
    .lookup<NativeFunction<Int32 Function(Int32, Int32)>>('native_add')
    .asFunction();

调用方法

为了验证集成是否成功,我们可以在plugin项目内的example子项目(这是自动生成的一个app类型的项目)内的lib/main.dart内尝试调用这个方法:

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

不过,大部分情况下,我们会把这个plugin引入到一个正常的app项目中:
举个例子,比如我们新建一个flutter app 项目,名字叫flutter_app,它与native_add这个plugin处在同一层级下,我们就可以在flutter_app项目的pubspec.yaml文件添加它对native_add的依赖:

dependencies:
	native_add:
		path: ../native_add/

flutter_app项目main.dart文件,引入native_add包:

import "package:native_add/native_add.dart";

最后,就可以直接调用集成的本地方法了
(看到这里你可能猜到了:是的,项目自动生成的example子项目依赖和导包这两步其实都已经为我们做过了)
好的,今天简单介绍了一下项目中如何集成本地代码并调用,更加深入的内容我们下一次继续研究,谢谢

猜你喜欢

转载自blog.csdn.net/Yaoobs/article/details/129206662