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》
      

 在前面的章节中,介绍结构体在 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_getNameMonster_getHPMonster_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 类,继承于 Opaque,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_setHPMonster_attack 等方法时,需要传递 Pointer<Monster> 指针;
  • 最后,由于是在 C 分配的内存,因此 Dart 需要在不使用的时候调用 calloc.free() 释放内存,避免内存泄漏;

3、扩展知识

 纯手工编写上面的一个类可能不算什么,但是如果有非常多的 C/C++ 代码需要映射到 Dart 使用时,可能就需要使用一些工具来自动生成代码了。
  
  官方推荐的一个代码自动生成工具:ffigen,地址:https://pub.dev/packages/ffigen.
  

4、总结

 
  上面介绍了如何把 C++ 中的类映射给 Dart 使用。后面的章节中,将会介绍数组、内存管理等知识,欢迎关注。
  
  

06、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. Flutter Ticker类的用法

    Flutter中Ticker类常隐式地用到动画中. 每个动画帧调用它的回调一次.创建时,首先禁用一个ticker.调用start以启用ticker.可以通过将muted设置为true使ticker不起 ...

  4. Flutter 标签类控件大全Chip

    老孟导读:Flutter内置了多个标签类控件,但本质上它们都是同一个控件,只不过是属性参数不同而已,在学习的过程中可以将其放在放在一起学习,方便记忆. RawChip Material风格标签控件,此 ...

  5. Flutter工具类 (二) 图片加载框架

    导入依赖 # 图片加载库cached_network_image: ^3.2.1 工具类 import 'dart:math';import 'package:cached_network_image ...

  6. C#笔记06 面向对象和类

    文章目录 封装和类 定义类 this 属性 上下文关键字value 自动属性 自定义构造器 对象初始化器 匿名对象 静态成员 设计模式(单例模式) readonly 嵌套类.分部类.分部方法 面向对象 ...

  7. java学习总结(16.06.07)类的静态成员和非静态成员

    java里,类的成员可分为静态成员和非静态成员(实例成员),静态成员和非静态成员,从定义上来说就是有没有static修饰符修饰的区别.有static修饰的成员就是静态成员.如 public stati ...

  8. 对话框(06):【类】QFontDialog [官翻]

    文章目录 详细说明 公共类型 enum QFontDialog::FontDialogOption 属性 公共函数 构造和析构 属性相关 其它 重写的公共函数 信号 静态公共函数 重写的受保护的函数 ...

  9. Flutter 实体类转String,String转实体类

    实体类转String方式 //引用需要转换的包 import 'dart:convert';//转换方法 String jsonStr = jsonEncode(myInfosBeans); Stri ...

  10. 必备技能06:UML类图要素及解析

    我们讲一个小学生在马路边捡到一分钱交给警察叔叔的故事,来分析类之间的几种关系 单独一个类的类图表示 首先定义一个简单的Person类: public class Person {private Str ...

最新文章

  1. jpa遇到的 org.hibernate.PersistentObjectException: detached entity passed to persist异常
  2. python3d动态图-Python图像处理之gif动态图的解析与合成操作详解
  3. C++利用构造函数限制对象的创建
  4. css3实现流星坠落效果
  5. 算法复杂度为O(N) 的排序算法
  6. Edittext不可编辑可点击,输入密码可见与不可见,验证码换格输入实现方法,车牌号自定义输入键盘
  7. 字符设备驱动高级篇2——字符设备驱动注册代码分析
  8. 用电脑更新手机ios系统_macOS 11正式版:大更新!苹果把iOS风格搬到了电脑上
  9. PD连接远程mysql_PowerDesigner连接远程Oracle数据库 | 学步园
  10. 全网首发:freeswitch无法正常绑定端口5060的解决办法
  11. 都在学奈飞 高盛要玩数据订阅
  12. 数据库系统概论学习总结
  13. 阿里云主要产品及功能介绍,阿里云产品分为6大分类:云计算基础/安全/大数据/人工智能/企业应用/物联网...
  14. Twaver-HTML5基础学习(1)两点一线
  15. 计算机网络中属于通信子网,计算机网络通常被划分为通信子网和资源子网,通信子网提供信息传输服务,资源子网提供共享资源。...
  16. linux中增加用户
  17. 杂志风城市夜景PPT模板
  18. 【工作笔记】Springboot一个比较通用的数据脱敏处理办法
  19. 7-18 二分法求多项式单根 (C语言)
  20. 使用JSON.parse,解决ie6-7上JSON未定义问题

热门文章

  1. 20200608模拟赛 Endless
  2. MacBook上有哪些相见恨晚的神器
  3. dub报invalid semver
  4. 关于使用克莱姆法则和矩阵消元法解线性方程组的效率的比较
  5. php生成临时文件,php生成zip压缩文件的方法详解
  6. Android投屏神器scrcpy
  7. 许三多买了中石油--暴笑股民语录许三多
  8. 领导力【管理学之五】
  9. 算法特训营第2周刷题题目
  10. Echarts地图实现点击某地区跳转到指定页面