Flutter FFI 学习笔记系列

  1. 《Flutter FFI 最简示例》
  2. 《Flutter FFI 基础数据类型》
  3. 《Flutter FFI 函数》
  4. 《Flutter FFI 字符串》
  5. 《Flutter FFI 结构体》
  6. 《Flutter FFI 类》
  7. 《Flutter FFI 数组》
  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 字符串的相互转换,还提到了内存的分配和释放。在后面的章节中,将会介绍结构体、数组、内存管理等知识,欢迎关注。
  
  

04、Flutter FFI 字符串相关推荐

  1. 01、Flutter FFI 最简示例

    Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...

  2. 03、Flutter FFI 函数

    Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...

  3. Ubuntu18.04 Flutter开发环境搭建

    目录 flutter安装 android studio安装 Android Studio创建Flutter项目 运行应用程序 flutter安装 下载flutter https://flutter.d ...

  4. flutter 返回指定界面_Flutter 即学即用系列博客——04 Flutter UI 初窥

    前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...

  5. flutter截取字符串_字符串截取的常用方法

    string str = "123abc456";        int i = 3; 1. 取字符串的前i个字符 str=str.Substring(0,i); 2. 去掉字符串 ...

  6. Flutter移动应用开发 - 04 Flutter 常用 widget 整理

    目录 1. 基础 widget 1.1 Text Text各种参数 多种效果合体 实例:俩花活(艺术字) 1.2 Row, Column row示例 主轴 调整大小 Expanded SizedBox ...

  7. flutter截取字符串_Flutter Dart List.map() 获取下标

    class HomePageState extends State{ final topTitles = ['审批单', '机票列表', '客服']; final topIcons = ['asset ...

  8. Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)

    前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...

  9. 2019 下半年 Flutter 实现的性能优化 | 英雄榜

    作者 / Yuqian Li & Shams Zakhour 高性能是 Flutter 的关键特性之一.我们希望通过本文着重分享在 2019 年下半年由整个 Flutter 社群实现的性能优化 ...

  10. 【Flutter学习】组件学习之目录

    01. Flutter组件-Layout-Container-容器  02. Flutter组件-Text-Text-文本  03. Flutter组件-Text-RichText-富文本  04. ...

最新文章

  1. Istio on ACK集成生态(2): 扩展AlertManager集成钉钉助力可观测性监控能力
  2. 语法之知识点的改进(Func/Action)
  3. 我的世界javamod怎么装_装暖气片,10个有8个人都想知道的这点事儿!
  4. 怎么用计算机拟合数据,数据拟合的几个应用实例-毕业论文.doc
  5. 解决方案_环网柜监测解决方案
  6. CoreOS ignition简介
  7. mysql死锁,等待资源,事务锁,Lock wait timeout exceeded; try restarting
  8. 电脑系统哪个好用_火绒杀毒,真有那么好用吗?
  9. 在微软平台上运行 SAP 应用程序
  10. WINDOWS用VS2010开发NPAPI插件步骤
  11. android离线天地图,天地图的移动App开发,离线地图下载不了
  12. 浅谈易用性测试及GUI常见的测试要求
  13. 儿童吹泡泡水简单配方_最好的吹泡泡水的制作方法
  14. iOS SceneDelegate使用总结
  15. python处理PNG的alpha通道
  16. 记一次npm发包失败的经历
  17. [《南国雪下》闲笔记事集]2010年12月15日 记雪
  18. C++之struct构造函数
  19. Spine 导出视频 音效事件
  20. es7版本数据备份与恢复(生产环境)

热门文章

  1. 正版星界边境服务器,《星界边境(Starbound)》正式版联机教程
  2. android手机定时截屏软件,最好用的截图软件 安卓手机截图软件横评对比
  3. Namenode服务挂
  4. CSDN博客排版格式入门
  5. mysql 删除临时表的语句_MySQL如何创建和删除临时表_MySQL
  6. 如何检测香港虚拟主机的访问速度及稳定性
  7. 中山医06年考研初试复试全攻略!( 完整版)
  8. echarts飞机航线图修改飞机的样式
  9. 计算机蓝牙传输,怎么使用电脑蓝牙 电脑蓝牙传输文件步骤【步骤教程】
  10. 6月3日 徒步虎跳峡——第一日