Dart FFI使用 示例

1,396 阅读2分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

是什么

Dart FFI(官方地址)是可以在Dart Native平台上运行的Dart移动、命令行和服务器应用上通过Dart FFI来调用C代码的一个技术。简单来说,就是Dart与C互相调用的一种机制。Dart FFI是Dart2.12.0版本后(同时包含在 Flutter 2.0 和以后的版本里),才作为稳定版本发布。

说到底,Dart语言也是因为Flutter使用了它才火起来的,所以Dart FFI技术在Flutter应用中更能发挥它更强大的作用

解决的问题

  1. 可以同步调用C API,不像Flutter Channel一开始就是异步
  2. 调用C语言更快,不像之前需要通过Native中转(或者改Flutter引擎代码)
  3. 还可以封装替换Flutter Channel达到更快和支持同步的目地

简单使用

为了只看FFI的特性,我先不在Flutter平台上使用,仅仅用命令行Dart应用的方式来讲解。 本人工程环境:

运行环境 MacOS 10.15.6

GCC 12.0.0

cmake 3.20.1

make 3.81

dart 2.14.2

1. 创建项目

由于项目结构简单,直接手动创建项目

1). 创建pubspec.yaml文件

2). 创建bin/main.dart文件

3). 创建C环境,创建librarylibrary/build文件夹

4). 创建library/sample.clibrary/sample.hlibrary/sample.defCMakeLists.txt文件

目录结构如下

|_ bin
    |_ main.dart
|_ library
    |_ build
    |_ CMakeLists.txt
    |_ sample.c
    |_ sample.h
    |_ sample.def
|_ pubspec.yaml

2. pubspec.yaml引入FFI

pubspec.yaml文件中的dependencies中加入ffipath

pubspec.yaml

name: ffi_sample
version: 0.0.1
description: 使用ffi及ffigen的例子

publish_to: none

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  path: ^1.7.0
  ffi: ^1.1.2

3. 编译C代码

sample.h中写简单的一个函数

sample.h

void hello_world();

sample.c中实现

sample.c

#include <stdio.h>
#include <stdlib.h>
#include "sample.h"
void hello_world()
{
    printf("Hello World\n");
}

sample.def中简单导出

LIBRARY   sample
EXPORTS
   sample

写编译使用的CMakeLists.txt文件

cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
project(sample_library VERSION 1.0.0 LANGUAGES C)
add_library(sample_library SHARED sample.c sample.def)
add_executable(sample_test sample.c)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)

set_target_properties(sample_library PROPERTIES
    PUBLIC_HEADER sample.h
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    OUTPUT_NAME "sample"
    XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Hex_Identity_ID_Goes_Here"
)

3. 编译C文件

现在所有文件都准备就绪,就可以编译C代码了。

1). 命令行进入到library/build文件夹下

2). 执行cmake ..生成编译所需文件

3). 执行make编译

cd library/build
cmake ..
make

如果在library/build文件夹下生成了libsample.1.0.0.dylib文件,那么说明编译成功了。

4. 写Dart通信代码

bin/main.dart中调用C

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'dart:io' show Platform, Directory;

import 'package:path/path.dart' as path;

void main() {
  void main() {
  // 初始化互调框架
  var libraryPath =
      path.join(Directory.current.path, 'ibrary', 'build', 'libsample.so');
  if (Platform.isMacOS) {
    libraryPath = path.join(
        Directory.current.path, 'library', 'build', 'libsample.dylib');
  }
  if (Platform.isWindows) {
    libraryPath =
        path.join(Directory.current.path, 'library', 'Debug', 'libsample.dll');
  }
  final dylib = DynamicLibrary.open(libraryPath);

  // *************** 1. Dart调用C方法 **************
  final Pointer<T> Function<T extends NativeType>(String symbolName) _lookup = dylib.lookup;

  late final _hello_worldPtr =
      _lookup<NativeFunction<Void Function()>>('hello_world');
  late final _hello_world = _hello_worldPtr.asFunction<void Function()>();
  // 调用C方法(无参)
  _hello_world();
}

5. 运行代码

现在,在命令行的项目根目录下运行

dart run bin/main.dart

如果输出

Hello World

说明调用成功

总结

上面就是Dart FFI简单的示例了,后面我会继续更新,详细介绍Dart FFI使用,欢迎关注。上面的代码我都打包放到Github了,Github地址