Flutter外部函数接口插件ffi的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter外部函数接口插件ffi的使用

简介

dart:ffi 是Dart和Flutter中用于与C代码互操作的库。它允许开发者直接调用C语言编写的动态链接库或共享对象中的函数。这对于需要高性能计算、访问操作系统特性或者使用现有C/C++库的情况非常有用。

特性

  • 支持基本类型转换(如 int, double)以及复杂结构体。
  • 提供了便捷的方法来处理字符串编码问题,比如将 Dart 字符串转换为 C 风格的字符串(null-terminated strings)。
  • 可以管理内存分配和释放,确保程序的安全性和稳定性。

安装

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  ffi: ^2.0.1 # 请根据实际情况选择最新版本

然后运行 flutter pub get 来安装包。

使用示例

下面是一个完整的示例,展示了如何使用 dart:ffi 来进行简单的内存操作和字符串转换。

示例代码

// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';
import 'package:ffi/ffi.dart';

void main() {
  // 分配并释放一些本地内存
  final pointer = calloc<Uint8>();
  try {
    // 设置指针指向的值
    pointer.value = 3;
    print('Pointer value is: ${pointer.value}');
    
    // 将 Dart 字符串编码为零终止的 UTF-8 字符串存入本地内存
    final myString = '😎👿💬';
    final charPointer = myString.toNativeUtf8();
    try {
      print('First byte is: ${charPointer.cast<Uint8>().value}');
      print('Converted back to Dart string: ${charPointer.toDartString()}');
    } finally {
      // 释放分配的内存
      calloc.free(charPointer);
    }
  } finally {
    // 确保在任何情况下都释放内存
    calloc.free(pointer);
  }
}

在这个例子中,我们做了以下几件事:

  1. 使用 calloc<Uint8>() 分配了一块本地内存,并设置其值为3。
  2. 使用 toNativeUtf8() 方法将一个包含特殊字符的 Dart 字符串编码为零终止的 UTF-8 字符串,并存储到本地内存中。
  3. 打印出第一个字节的内容以及从本地内存中读取回来的 Dart 字符串。
  4. 最后,确保通过 calloc.free() 函数释放所有分配的本地内存,避免内存泄漏。

注意:当使用 FFI 进行跨语言调用时,一定要小心处理内存管理,以防止出现内存泄漏等问题。

更多资源

希望这个指南能帮助你更好地理解和使用 dart:ffi!如果你有任何问题或需要进一步的帮助,请随时提问。


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

1 回复

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


当然,以下是一个关于如何在Flutter中使用外部函数接口(FFI)插件的示例。这个示例将展示如何创建一个简单的Flutter应用,它通过FFI调用一个本地(C语言编写)的库函数。

步骤 1: 设置Flutter项目

首先,确保你已经安装了Flutter和Dart SDK。然后,创建一个新的Flutter项目:

flutter create ffi_example
cd ffi_example

步骤 2: 添加ffi依赖

pubspec.yaml文件中添加ffipath_provider依赖:

dependencies:
  flutter:
    sdk: flutter
  ffi: ^1.0.0  # 请检查最新版本号
  path_provider: ^2.0.0  # 请检查最新版本号

然后运行flutter pub get来安装依赖。

步骤 3: 创建本地C库

ffi_example/android/app/src/main/cpp目录下创建一个名为native-lib.c的文件(如果没有cpp目录,请手动创建):

// native-lib.c
#include <jni.h>
#include <string.h>

// 声明一个简单的函数
const char* hello_from_c() {
    return "Hello from C!";
}

同时,创建一个CMakeLists.txt文件来构建这个库:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             native-lib.c )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

步骤 4: 配置Flutter以使用本地库

ffi_example/android/app/build.gradle文件中,确保externalNativeBuild块被正确配置:

android {
    ...
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

ffi_example/ios/Runner/Info.plist中添加对本地库的引用(这一步对于iOS是可选的,因为iOS通常使用Objective-C或Swift,而不是直接链接C库,但本示例仅关注Android)。

步骤 5: 使用FFI在Flutter中调用本地函数

创建一个Dart文件(例如ffi_wrapper.dart)来封装FFI调用:

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:path_provider/path_provider.dart';

typedef NativeHelloFromC = Int8 Function();
typedef HelloFromC = String Function();

class NativeLibrary {
  late DynamicLibrary _lib;

  NativeLibrary() {
    _openLibrary();
  }

  void _openLibrary() {
    if (Platform.isAndroid) {
      _lib = DynamicLibrary.open("libnative-lib.so");
    } else if (Platform.isIOS) {
      // 对于iOS,你需要通过不同的方式加载库,这里省略。
      throw UnsupportedError("iOS is not supported in this example.");
    } else {
      throw UnsupportedError("This platform is not supported.");
    }
  }

  HelloFromC lookupHelloFromC() {
    return _lib
        .lookup<NativeFunction<NativeHelloFromC>>('hello_from_c')
        .asFunction<HelloFromC>();
  }
}

在你的主Dart文件(例如main.dart)中使用这个封装类:

import 'package:flutter/material.dart';
import 'ffi_wrapper.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: FutureBuilder<String>(
            future: _getHelloFromC(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                } else {
                  return Text('Message from C: ${snapshot.data}');
                }
              } else {
                return CircularProgressIndicator();
              }
            },
          ),
        ),
      ),
    );
  }

  Future<String> _getHelloFromC() async {
    final NativeLibrary nativeLibrary = NativeLibrary();
    final HelloFromC helloFromC = nativeLibrary.lookupHelloFromC();
    return helloFromC();
  }
}

总结

这个示例展示了如何在Flutter应用中使用FFI来调用本地C库中的函数。请注意,iOS平台的配置和调用可能会有所不同,并且需要更多的步骤来确保库被正确加载和链接。对于生产级应用,还需要考虑更多关于内存管理、错误处理和性能优化的问题。

回到顶部