IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 04、Flutter FFI 字符串 -> 正文阅读

[移动开发]04、Flutter FFI 字符串

?
Flutter FFI 学习笔记系列

  1. 《Flutter FFI 最简示例》
  2. 《Flutter FFI 基础数据类型》
  3. 《Flutter FFI 函数》
  4. 《Flutter FFI 字符串》
  5. 《Flutter FFI 结构体》
  6. 《Flutter FFI 类》
  7. 《FlutterFFI 数组》
  8. 《Flutter FFI 内存管理》
  9. 《Flutter FFI Dart Native API》
    ? 
    ?

在前面的章节中,介绍了基础数据类型和函数的知识,在这一章节中,将介绍 Dart 与 C 语言的字符串传递方式。
? 
?

1、C 语言返回字符串给 Dart

1.1 C 语言字符串

? C语言中的字符串是以 “\0” 为结束标记的,假设 C 语言中定义了一个 greetingString() 函数,返回了问候语:

#include <stdint.h>

#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))

DART_API const char *greetString() {
    return "Hello from Native\0";
}

? 那么,我们如何在 Dart 中使用该函数返回的字符串呢?
?

1.2 引入 ffi 库

? 在 Dart 中,可以引用 ffi 库来帮助我们快速实现字符串转换功能。
? 首先,打开 pubspec.yaml 文件,增加依赖库:

dependencies:
  ffi: ^1.0.0

? 然后,执行 pub get ,下载依赖。此时便可以在 Dart 代码中引入 ffi 库:

import 'package:ffi/ffi.dart';

?

1.3 C字符串转Dart字符串

? 下面接下来看一下如何使用ffi 库把 C 的字符串映射到 Dart 中使用。
? 
? 首先,在 Dart 中定义两个函数类型,用于映射 C 中的 greetingString() 函数:

typedef Native_greetingString = Pointer<Int8> Function();

typedef FFI_greetingString = Pointer<Int8> Function();

? 接着,在 Dat 中编写代码调用 C 函数,并将 C 的字符串转为 Dart 的字符串:

//加载符号
DynamicLibrary nativeApi = Platform.isAndroid
    ? DynamicLibrary.open("libnative_ffi.so")
    : DynamicLibrary.process();
    
//查找目标函数
FFI_greetingString greetingFunc = nativeApi
	.lookupFunction<Native_greetingString, FFI_greetingString>("greetString");
    
//调用 greetString 函数,并将结果转为 Dart String.
Pointer<Int8> result = greetingFunc();
String greeting = result.cast<Utf8>().toDartString();

//打印结果
print("greeting=$greeting");
    
//输出结果
//greeting=Hello from Native

? 代码说明:

  • C 语言中的字符串用 char* 表示,字符串是以 “\0” 为结束标记;
  • Dart 需要使用 Pointer<Int8> 表示 C 语言中的 char* 类型;
  • toDartString() 是 ffi 库提供的函数,用于将 Pointer<Utf8> 转为 Dart String。因此需要将 Pointer<Int8> 转为 Pointer<Utf8> 类型。当然,也可以直接用 Pointer<Utf8> 类型表示 C 中的字符串;
  • greetString()返回的是一个字符串常量,因此不需要释放内存。

? toDartString() 的工作原理是把 cahr* 转为 char 数组,然后采用 utf8 进行解码。该函数定义如下:

/// Extension method for converting a`Pointer<Utf8>` to a [String].
extension Utf8Pointer on Pointer<Utf8> {
  /// The number of UTF-8 code units in this zero-terminated UTF-8 string.
  ///
  /// The UTF-8 code units of the strings are the non-zero code units up to the
  /// first zero code unit.
  int get length {
    _ensureNotNullptr('length');
    final codeUnits = cast<Uint8>();
    return _length(codeUnits);
  }

  /// Converts this UTF-8 encoded string to a Dart string.
  ///
  /// Decodes the UTF-8 code units of this zero-terminated byte array as
  /// Unicode code points and creates a Dart string containing those code
  /// points.
  ///
  /// If [length] is provided, zero-termination is ignored and the result can
  /// contain NUL characters.
  ///
  /// If [length] is not provided, the returned string is the string up til
  /// but not including  the first NUL character.
  String toDartString({int? length}) {
    _ensureNotNullptr('toDartString');
    final codeUnits = cast<Uint8>();
    if (length != null) {
      RangeError.checkNotNegative(length, 'length');
    } else {
      length = _length(codeUnits);
    }
    return utf8.decode(codeUnits.asTypedList(length));
  }

  static int _length(Pointer<Uint8> codeUnits) {
    var length = 0;
    while (codeUnits[length] != 0) {
      length++;
    }
    return length;
  }

  void _ensureNotNullptr(String operation) {
    if (this == nullptr) {
      throw UnsupportedError(
          "Operation '$operation' not allowed on a 'nullptr'.");
    }
  }
}

? 说明:

  • 除了 Utf8 之外,ffi 库还提供了 Utf16,有兴趣可以了解一下。
    ?

2、Dart 返回字符串给 C

? 上面介绍了如何把 C 的字符串转为 Dart 的字符串,接下来看看反过来是怎样实现的。
? 
? 首先,在 C 中定义两个函数:

  • reverse_string() 用于反转字符串;
  • free_string() 用于释放字符串所占用的内存;
#include <stdint.h>

#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
    
DART_API char *reverse_string(const char *str, int length)
{
    // Allocates native memory in C.
    char *reversed_str = (char *)malloc((length + 1) * sizeof(char));
    for (int i = 0; i < length; i++)
    {
        reversed_str[length - i - 1] = str[i];
    }
    reversed_str[length] = '\0';
    return reversed_str;
}

void free_string(char *str)
{
    // Free native memory in C which was allocated in C.
    free(str);
}

? 然后,在 Dart 中定义与 C 相对应的函数类型:

typedef Native_reverseString = Pointer<Int8> Function(Pointer<Int8>, Int32);
typedef FFI_reverseString = Pointer<Int8> Function(Pointer<Int8>, int);

typedef Native_freeString = Void Function(Pointer<Int8>);
typedef FFI_freeString = void Function(Pointer<Int8>);

? 最后,在 Dart 调用 C 的函数,将 Dart 字符串转成 C 的字符串,并进行内存释放;

//加载库
DynamicLibrary nativeApi = Platform.isAndroid
    ? DynamicLibrary.open("libnative_ffi.so")
    : DynamicLibrary.process();

//查找目标函数 - reverse_string
FFI_reverseString reverseFunc = nativeApi
    .lookupFunction<Native_reverseString, FFI_reverseString>("reverse_string");

//查找目标函数 - free_string
FFI_freeString freeFunc = nativeApi
    .lookupFunction<Native_freeString, FFI_freeString>("free_string");

//调用函数
String value = "ABCDEFG";
Pointer<Int8> nativeValue = value.toNativeUtf8().cast<Int8>();
Pointer<Int8> reverseValue = reverseFunc(nativeValue, value.length);

print("original.value=$value");
print("reverse.value=${reverseValue.cast<Utf8>().toDartString()}");

//释放字符串所占用的内存
freeFunc(nativeValue); //或者调用 calloc.free(nativeValue);
freeFunc(reverseValue); //或者调用 calloc.free(reverseValue);

//输出结果:
//original.value=ABCDEFG
//reverse.value=GFEDCBA

说明:

  • 因为 C 中的 reverse_string() 使用 malloc 分配了内存,因此在使用完之后,必须使用 free 释放内存,否则会有内存泄漏;
  • toNativeUtf8() 是由 ffi 库提供的API,调用该函数时会在 Native 中分配内存,因此使用完后也需要释放内存。
    也可以使用 calloc.free() 来释放由 malloc 分配的内存;

? toNativeUtf8() 函数的作用将 Dart 字符串转为 Pointer<Utf8> ,以便在 C 中使用 Dart 字符串。
? toNativeUtf8() 其工作原理是把 Dart 字符串转为 Utf8,然后在 C 中分配一个 char 数组,接着再复制 Utf8 的内存数据。该函数的定义如下:

/// Extension method for converting a [String] to a `Pointer<Utf8>`.
extension StringUtf8Pointer on String {
  /// Creates a zero-terminated [Utf8] code-unit array from this String.
  ///
  /// If this [String] contains NUL characters, converting it back to a string
  /// using [Utf8Pointer.toDartString] will truncate the result if a length is
  /// not passed.
  ///
  /// Unpaired surrogate code points in this [String] will be encoded as
  /// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in
  /// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
  ///
  /// Returns an [allocator]-allocated pointer to the result.
  Pointer<Utf8> toNativeUtf8({Allocator allocator = malloc}) {
    final units = utf8.encode(this);
    final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
    final Uint8List nativeString = result.asTypedList(units.length + 1);
    nativeString.setAll(0, units);
    nativeString[units.length] = 0;
    return result.cast();
  }
}

? 说明:

  • 同样的,ffi 库也提供了 Utf16toNativeUtf16,有兴趣可以了解一下。

?

3、总结

? 
? 上面介绍 C 字符串与 Dart 字符串的相互转换,还提到了内存的分配和释放。在后面的章节中,将会介绍结构体、数组、内存管理等知识,欢迎关注。
? 
? 
?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 07:56:17  更:2021-07-28 07:56:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 9:35:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码