06、Flutter FFI 类
Flutter FFI 学习笔记系列
- 《Flutter FFI 最简示例》
- 《Flutter FFI 基础数据类型》
- 《Flutter FFI 函数》
- 《Flutter FFI 字符串》
- 《Flutter FFI 结构体》
- 《Flutter FFI 类》
- 《Flutter FFI 数组》
- 《Flutter FFI 内存管理》
- 《Flutter FFI Dart Native API》
在前面的章节中,介绍结构体在 C 和 Dart 中的相互调用。接下来将介绍类在 C 和 Dart 中的相互调用。
由于 Dart 只能调用 C 风格的符号,并不能调用 C++ 风格的符号,而 class 是 C++ 才有的,因此想要在 Dart 调用 C++ 的类,需要做一些额外的工作才行。
1、基本思路
基本思路如下:
- 类的映射:写一个类,继承
Opaque
,用于表示 C++ 中的类; - 成员变量的映射:通过全局方法实现;
- 成员方法的映射:通过全局方法实现;
2、示例
下面的示例中,演示了如何将 C++ 中的类映射给 Dart 使用。
首先,在 C/C++ 中定义类,然后定义一些全局函数,如下:
#include <malloc.h>
#include <cstring>#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))//定义一个类表示怪物
class Monster {public:char *name{}; //名称int32_t hp = 255; //血量int32_t atk = 10; //攻击力public://攻击目标void attack(Monster *target) const {target->hp -= atk;}
};//创建一个怪物
DART_API Monster *createMonster(char *name, int32_t hp, int32_t atk) {auto *monster = (Monster *) malloc(sizeof(Monster));monster->hp = hp;monster->atk = atk;monster->name = name;return monster;
}DART_API const char *Monster_getName(Monster *monster) {return monster->name;
}
DART_API int32_t Monster_getHP(Monster *monster) {return monster->hp;
}
DART_API void Monster_setHP(Monster *monster, int32_t hp) {monster->hp = hp;
}
DART_API int32_t Monster_getATK(Monster *monster) {return monster->atk;
}
DART_API void Monster_setATK(Monster *monster, int32_t atk) {monster->atk = atk;
}
DART_API void Monster_attack(Monster *monster, Monster *target) {monster->attack(target);
}
代码说明:
- 上面的 C 代码中,定义了一个名为
Monster
的类,包括name
,hp
,atk
三个属性,和一个attack()
方法; - 对于 类中的成员变量,需要定义对应的 C 风格的函数,如:
Monster_getName
、Monster_getHP
、Monster_setHP
等; - 对应 类中的成员方法,同样需要定义对应的 C 风格的函数,如:
Monster_attack()
。
接着,在 Dart 代码定义相对应的函数类型 和Opaque
类型 如下:
//-------下面是函数定义-------typedef Native_createMonster = Pointer<Monster> Function(Pointer<Utf8> name, Int32 hp, Int32 atk);
typedef FFI_createMonster = Pointer<Monster> Function(Pointer<Utf8> name, int hp, int atk);typedef Native_getName = Pointer<Utf8> Function(Pointer<Monster> monster);
typedef FFI_getName = Pointer<Utf8> Function(Pointer<Monster> monster);typedef Native_setHP = Void Function(Pointer<Monster> monster, Int32 hp);
typedef FFI_setHP = void Function(Pointer<Monster> monster, int hp);typedef Native_getHP = Int32 Function(Pointer<Monster> monster);
typedef FFI_getHP = int Function(Pointer<Monster> monster);typedef Native_setATK = Void Function(Pointer<Monster> monster, Int32 atk);
typedef FFI_setATK = void Function(Pointer<Monster> monster, int atk);typedef Native_getATK = Int32 Function(Pointer<Monster> monster);
typedef FFI_getATK = int Function(Pointer<Monster> monster);typedef Native_attack = Int32 Function(Pointer<Monster> monster, Pointer<Monster> target);
typedef FFI_attack = int Function(Pointer<Monster> monster, Pointer<Monster> target);// ----------- 下面是类的定义 -------------//一个Monster类,对应于C中的Monster
class Monster extends Opaque {static FFI_createMonster? createFunc;static FFI_getName? nameFunc;static FFI_setHP? setHPFunc;static FFI_getHP? getHPFunc;static FFI_setATK? setATKFunc;static FFI_getATK? getATKFunc;static FFI_attack? attackFunc;static init(DynamicLibrary dl) {createFunc = dl.lookupFunction<Native_createMonster, FFI_createMonster>("createMonster");nameFunc = dl.lookupFunction<Native_getName, FFI_getName>("Monster_getName");setHPFunc = dl.lookupFunction<Native_setHP, FFI_setHP>("Monster_setHP");getHPFunc = dl.lookupFunction<Native_getHP, FFI_getHP>("Monster_getHP");setATKFunc = dl.lookupFunction<Native_setATK, FFI_setATK>("Monster_setATK");getATKFunc = dl.lookupFunction<Native_getATK, FFI_getATK>("Monster_getATK");attackFunc = dl.lookupFunction<Native_attack, FFI_attack>("Monster_attack");}//保存由C返回的实例的指针late Pointer<Monster> _thiz;late Pointer<Utf8> nativeNameValue;Monster(String name, int hp, int atk) {nativeNameValue = name.toNativeUtf8();//创建Monster实例,并保存实例指针_thiz = createFunc!(nativeNameValue, hp, atk); }String get name => nameFunc!(_thiz).toDartString();int get hp => getHPFunc!(_thiz);set hp(value) => setHPFunc!(_thiz, value);int get atk => getATKFunc!(_thiz);set atk(value) => setATKFunc!(_thiz, value);void attack(Monster target) {attackFunc!(_thiz, target._thiz);}void free() {calloc.free(nativeNameValue);calloc.free(_thiz);}String toDebugString() {return "{name=$name, hp=$hp, atk=$atk}";}
}
说明:
- 在 Dart 中,我们定义了一个
Monster
类,继承于Opaqu
e,Opaque
的意思是不透明,即其成员是不暴露的; init()
方法是一个初始化方法,可以提前把我们需要用到的函数提前映射好,方便后续使用;- 在
Monster
类中,我们通过定义setter
/getter
来表示成员变量。它们的实现是调用 C 中的全局方法,把实例(Monster
指针)传给这些全局方法,这样这些方法就知道对哪个实例调用相应的方法了; - 在
Monster
类中,我们定义了与 C 一致的成员方法。它们的实现也是调用 C 中的全局方法; - 最后,在
Monster
实例不使用的时候,可以调用free()
方法进行内存释放。
最后,我们就可以在 Dart 中使用该Monster
类了,使用方法如下:
//加载符号
DynamicLibrary nativeApi = Platform.isAndroid? DynamicLibrary.open("libnative_ffi.so"): DynamicLibrary.process();//初始化相关函数
Monster.init(nativeApi);//创建两个Monster
Monster alice = Monster("Alice", 255, 10);
Monster nero = Monster("Nero", 200, 12);print("before fighting, ${alice.toDebugString()}, ${nero.toDebugString()}");//让两个Monster相互攻击
for (int i = 0; i < 10; i++) {if (i.isEven) {alice.attack(nero);print("Alice =>>>>> Nero, ${nero.toDebugString()}");} else {nero.attack(alice);print("Alice <<<<<= Nero, ${alice.toDebugString()}");}
}print("after fighting, ${alice.toDebugString()}, ${nero.toDebugString()}");//最后不要忘记释放内存
alice.free();
nero.free();
代码说明:
- Dart 调用
createMonster
创建了Monster
实例之后,需要将实例的指针Pointer<Monster>
保存起来,以便后续使用; - 调用
Monster_setHP
、Monster_attack
等方法时,需要传递Pointer<Monster>
指针; - 最后,由于是在 C 分配的内存,因此 Dart 需要在不使用的时候调用
calloc.free()
释放内存,避免内存泄漏;
3、扩展知识
纯手工编写上面的一个类可能不算什么,但是如果有非常多的 C/C++ 代码需要映射到 Dart 使用时,可能就需要使用一些工具来自动生成代码了。
官方推荐的一个代码自动生成工具:ffigen,地址:https://pub.dev/packages/ffigen.
4、总结
上面介绍了如何把 C++ 中的类映射给 Dart 使用。后面的章节中,将会介绍数组、内存管理等知识,欢迎关注。
06、Flutter FFI 类相关推荐
- 01、Flutter FFI 最简示例
Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...
- 03、Flutter FFI 函数
Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...
- Flutter Ticker类的用法
Flutter中Ticker类常隐式地用到动画中. 每个动画帧调用它的回调一次.创建时,首先禁用一个ticker.调用start以启用ticker.可以通过将muted设置为true使ticker不起 ...
- Flutter 标签类控件大全Chip
老孟导读:Flutter内置了多个标签类控件,但本质上它们都是同一个控件,只不过是属性参数不同而已,在学习的过程中可以将其放在放在一起学习,方便记忆. RawChip Material风格标签控件,此 ...
- Flutter工具类 (二) 图片加载框架
导入依赖 # 图片加载库cached_network_image: ^3.2.1 工具类 import 'dart:math';import 'package:cached_network_image ...
- C#笔记06 面向对象和类
文章目录 封装和类 定义类 this 属性 上下文关键字value 自动属性 自定义构造器 对象初始化器 匿名对象 静态成员 设计模式(单例模式) readonly 嵌套类.分部类.分部方法 面向对象 ...
- java学习总结(16.06.07)类的静态成员和非静态成员
java里,类的成员可分为静态成员和非静态成员(实例成员),静态成员和非静态成员,从定义上来说就是有没有static修饰符修饰的区别.有static修饰的成员就是静态成员.如 public stati ...
- 对话框(06):【类】QFontDialog [官翻]
文章目录 详细说明 公共类型 enum QFontDialog::FontDialogOption 属性 公共函数 构造和析构 属性相关 其它 重写的公共函数 信号 静态公共函数 重写的受保护的函数 ...
- Flutter 实体类转String,String转实体类
实体类转String方式 //引用需要转换的包 import 'dart:convert';//转换方法 String jsonStr = jsonEncode(myInfosBeans); Stri ...
- 必备技能06:UML类图要素及解析
我们讲一个小学生在马路边捡到一分钱交给警察叔叔的故事,来分析类之间的几种关系 单独一个类的类图表示 首先定义一个简单的Person类: public class Person {private Str ...
最新文章
- jpa遇到的 org.hibernate.PersistentObjectException: detached entity passed to persist异常
- python3d动态图-Python图像处理之gif动态图的解析与合成操作详解
- C++利用构造函数限制对象的创建
- css3实现流星坠落效果
- 算法复杂度为O(N) 的排序算法
- Edittext不可编辑可点击,输入密码可见与不可见,验证码换格输入实现方法,车牌号自定义输入键盘
- 字符设备驱动高级篇2——字符设备驱动注册代码分析
- 用电脑更新手机ios系统_macOS 11正式版:大更新!苹果把iOS风格搬到了电脑上
- PD连接远程mysql_PowerDesigner连接远程Oracle数据库 | 学步园
- 全网首发:freeswitch无法正常绑定端口5060的解决办法
- 都在学奈飞 高盛要玩数据订阅
- 数据库系统概论学习总结
- 阿里云主要产品及功能介绍,阿里云产品分为6大分类:云计算基础/安全/大数据/人工智能/企业应用/物联网...
- Twaver-HTML5基础学习(1)两点一线
- 计算机网络中属于通信子网,计算机网络通常被划分为通信子网和资源子网,通信子网提供信息传输服务,资源子网提供共享资源。...
- linux中增加用户
- 杂志风城市夜景PPT模板
- 【工作笔记】Springboot一个比较通用的数据脱敏处理办法
- 7-18 二分法求多项式单根 (C语言)
- 使用JSON.parse,解决ie6-7上JSON未定义问题