Flutter原生函数接口插件universal_ffi的使用

Flutter原生函数接口插件universal_ffi的使用

简介

universal_ffi 是一个基于 wasm_ffidart:ffi 的包装器,旨在提供跨平台一致的API。它还包含一些辅助方法,使使用更加方便。

特性

  • 动态库异步加载DynamicLibrary.openAsync() 方法使得 dart:ffiwasm_ffi 都可以异步加载动态库。
  • 模块路径解析FfiHelper.load() 方法可以解析模块路径,并根据不同的平台加载相应的库文件。
  • 内存管理:提供了 FfiHelper.safeUsingFfiHelper.safeWithZoneArena 方法,确保使用特定库的内存。

安装

dart pub add universal_ffi

或者

flutter pub add universal_ffi

生成绑定文件

使用 package:ffigen 生成绑定文件,并将 import 'dart:ffi' as ffi; 替换为 import 'package:universal_ffi/ffi.dart' as ffi;

使用 FfiHelper

以下是一个简单的示例,展示了如何使用 FfiHelper 加载原生库并调用其函数:

import 'package:universal_ffi/ffi.dart';
import 'package:universal_ffi/ffi_helper.dart';
import 'package:universal_ffi/ffi_utils.dart';
import 'native_example_bindings.dart';

class Example {
  final FfiHelper helper;
  final NativeExampleBindings bindings;

  Example._(this.helper) : bindings = NativeExampleBindings(helper.library);

  static Future<Example> create(String libPath) async {
    final helper = await FfiHelper.load(libPath);
    return Example._(helper);
  }

  String getLibraryName() =>
      bindings.getLibraryName().cast<Utf8>().toDartString();

  String hello(String name) {
    return helper.safeUsing(
      (Arena arena) {
        final cString = name.toNativeUtf8(allocator: arena).cast<Char>();
        return bindings.hello(cString).cast<Utf8>().toDartString();
      },
    );
  }

  int intSize() => bindings.intSize();

  int boolSize() => bindings.boolSize();

  int pointerSize() => bindings.pointerSize();
}

void main() async {
  final example = await Example.create('path/to/native_library');

  print('Library Name: ${example.getLibraryName()}');
  print('Hello, World!: ${example.hello('World')}');
  print('int size: ${example.intSize()}');
  print('bool size: ${example.boolSize()}');
  print('pointer size: ${example.pointerSize()}');
}

动态库异步加载

DynamicLibrary.openAsync() 方法使得 dart:ffiwasm_ffi 都可以异步加载动态库。这对于跨平台开发非常有用。

FfiHelper.load()

FfiHelper.load() 方法可以根据不同的平台解析模块路径并加载相应的库文件。支持的选项包括:

  • 简单使用:假设所有平台从相同的相对路径加载共享库。
  • isStaticallyLinked:指定是否静态链接。
  • isFfiPlugin:用于 Flutter FFI 插件。
  • Overrides:为特定平台指定模块路径。

多个 wasm_ffi 模块

如果在同一个项目中有多个 wasm_ffi 模块,全局内存将只引用第一个加载的模块。为了避免意外行为,可以显式使用 library.allocator 或者使用 FfiHelper.safeUsingFfiHelper.safeWithZoneArena 方法。

贡献

欢迎贡献!🚀

希望这个示例能帮助你更好地理解和使用 universal_ffi 插件。如果有任何问题或建议,请随时提出。


更多关于Flutter原生函数接口插件universal_ffi的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter原生函数接口插件universal_ffi的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用universal_ffi插件来调用原生函数接口的示例代码。universal_ffi插件允许你在Flutter中通过FFI(Foreign Function Interface)来调用C/C++等原生代码。

步骤 1: 配置项目

首先,你需要配置你的Flutter项目来使用universal_ffi插件。

  1. pubspec.yaml文件中添加依赖:
dependencies:
  flutter:
    sdk: flutter
  universal_ffi: ^0.x.x  # 请替换为最新版本号
  1. 运行flutter pub get来安装依赖。

步骤 2: 创建原生库

创建一个简单的C/C++库,例如native_lib.c

// native_lib.c
#include <stdint.h>

int32_t add(int32_t a, int32_t b) {
    return a + b;
}

编译这个C文件生成共享库(例如,在Linux上生成.so文件,在macOS上生成.dylib文件,在Windows上生成.dll文件)。

步骤 3: 在Flutter中加载原生库

在Flutter项目中,你需要编写Dart代码来加载并调用这个原生库。

  1. 创建一个Dart文件,例如ffi_loader.dart
import 'dart:ffi';
import 'dart:typed_data';
import 'package:universal_ffi/universal_ffi.dart';

// 定义原生函数类型
typedef NativeAddFunc = Int32 Function(Int32 a, Int32 b);

// 加载原生库
class NativeLibraryLoader {
  late DynamicLibrary _nativeLibrary;
  late NativeAddFunc _addFunc;

  NativeLibraryLoader() {
    // 根据平台选择合适的库文件路径
    var libraryPath = Platform.isAndroid
        ? 'libnative_lib.so'
        : Platform.isIOS
        ? 'libnative_lib.dylib'
        : Platform.isWindows
        ? 'native_lib.dll'
        : 'libnative_lib.so';  // 默认情况(例如Linux)

    // 使用Universal FFI加载库
    var ffi = Ffi.fromAsset(libraryPath);
    _nativeLibrary = ffi.library!;

    // 获取原生函数指针
    _addFunc = _nativeLibrary
        .lookup<NativeFunction<NativeAddFunc>>('add')
        .uncheckedCast<NativeAddFunc>();
  }

  int add(int a, int b) {
    return _addFunc(a, b);
  }
}
  1. lib目录下创建一个assets文件夹,并将编译好的原生库文件(例如libnative_lib.so)放在其中。

  2. pubspec.yaml中声明库文件作为资产:

flutter:
  assets:
    - assets/libnative_lib.so
    - assets/libnative_lib.dylib  # 如果需要支持iOS
    - assets/native_lib.dll       # 如果需要支持Windows

步骤 4: 使用原生函数

现在你可以在你的Flutter应用中使用这个原生函数了。

import 'package:flutter/material.dart';
import 'ffi_loader.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('FFI Example'),
        ),
        body: Center(
          child: NativeFunctionExample(),
        ),
      ),
    );
  }
}

class NativeFunctionExample extends StatefulWidget {
  @override
  _NativeFunctionExampleState createState() => _NativeFunctionExampleState();
}

class _NativeFunctionExampleState extends State<NativeFunctionExample> {
  late NativeLibraryLoader _nativeLibraryLoader;
  late int _result;

  @override
  void initState() {
    super.initState();
    _nativeLibraryLoader = NativeLibraryLoader();
    _result = _nativeLibraryLoader.add(5, 3);
  }

  @override
  Widget build(BuildContext context) {
    return Text('Result of add(5, 3): $_result');
  }
}

这个示例展示了如何在Flutter中使用universal_ffi插件来加载和调用C语言编写的原生函数。根据你的实际情况,你可能需要调整路径和函数名等配置。

回到顶部