Dart对C语言的调用我个人感觉比Java的jni要简单得多,用不着还在C语言中创建出对应的函数才能调用,Dart都能直接调用C语言原有的那些函数,因为java jni调用C语言的完整流程我也上手写过,Dart还是简易多了,不用打乱已有C语言的结构
上一篇
Dart:ffi上手一:So库编译篇
在使用ffi的时候,只需要把C语言或者C++语言的方法或者函数与Dart中一一对应起来
在上手ffi了半天,官方的example弄到Flutter项目报错,提示import ‘package:ffi/ffi.dart’;没有找到,我还以为是Flutter中的Dart没有这个包,因为我想到最初Dart支持ffi的时候Flutter中的Dart Sdk没有,然后自己**了半天,发现这是个Dart Packages
所以
ffi: ^0.1.3
由于是在Flutetr中使用ffi,并不是在纯Dart中,所以C语言的任何Printf都是看不到的,不会反馈到Flutter的控制台
void hello_word(){
printf("hello word");
}
编译好后,重新调试一下App,才会更新新编译的So库
需要这两个包的导入
import 'dart:ffi';
import 'package:ffi/ffi.dart';
参照官方,如果要调用一个C语言函数写上两个typedef,这的确会带来很多方便
typedef hello_word=Void Function();
这里一定要注意,Void的V是V不是v,这部分声明C语言中的函数,涉及到的所有类型必须全部用ffi中对应的NativeType,而这个原生函数的声明后不是拿来调用的,切记,不是拿来调用的,是要用它跟下面Dart的方法绑定起来
接下来
typedef HelloWord=void Function();
这就是Dart中需要调用的方法,也有坑,这部分的原则就是,如果Dart有原生的那种类型就用dart已经有的类型,如果没有,还是得用NativeType
举个例子
原生函数
int add(int a,int b)
这个时候对应的dart函数该怎么写呢,原生的int,dart也有,所以dart韩式也是返回int,形参中也都是int,所以
typedef Add=int Function(int a,int b);
涉及到其他类型下面说,接下来
final dylib = DynamicLibrary.open("libterm.so");
这行的作用就是找到对应的so库,记住这儿是相对路径,找的就是打包进apk中的so,与libflutter.so同级
它的作用跟java中jni的loadlibrary类似
导入了一个动态库
找到原生方法的指针
final helloWordPointer = dylib
.lookup>('hello_word');
用了刚才的dylib对象的lookup方法,中间的一堆<<>>都是泛型,NativeFunction的泛型就是第一个typedef,也是第一个typedef唯一用到的地方,最后lookup需要传入一个字符串,这个字符串就语言中的方法或者函数的名字,它返回的是一个Pointer类型,就是指针的意思,dart用了自己对应的类型来表示原生中的一些类型
然后定义并且初始化第二个typedef的方法
HelloWord helloWord = helloWordPointer.asFunction();
通过Ponter对象的asFunction()方法初始化了这个helloWord对象,这个helloWord就是就是一个普通的void
调用它
helloword();
这部分的打印是看不到的
C:
int add(int a,int b){
return a+b;
}
上面也提到了这个
两个typedef
typedef add_func=Int32 Function(Int32 a,Int32 b);
typedef Add=int Function(int a,int b);
执行代码
var path = 'libterm.so';
final dylib = DynamicLibrary.open(path);
final addPinter = dylib.lookup>("add");
Add add = addPinter.asFunction();
print("${add(1,2)}");
print("${add(3,5)}");
print("${add(6,9)}");
控制台输出,避免内存泄漏,所有的指针类型用完需要调用
free(pointer);
这个相对来说难一点也麻烦一点,不过ffi官方文档有这个例子,挺不错的,我直接拿来用,dart中对应c char的类型是Utf8,
Utf8.toUtf8("")返回指针,即对应原生的char *,用这玩意注意释放,不要把它当匿名对象使用,把它保存在一个对象里面,不然不好控制内存的释放
Utf8.fromUtf8(pointer)返回str,切记,这个坑就是它只能转换最后以\0结尾的字符
补上自己的注释
char *reverse(char *str, int length)
{
//动态申请length+1个长度的字符空间
char *reversed_str = (char *)malloc((length + 1) * sizeof(char));
//简单的倒置算法
for (int i = 0; i < length; i++)
{
reversed_str[length - i - 1] = str[i];
}
//这行很关键,切记,让它最后一个为\0
reversed_str[length] = '\0';
return reversed_str;
}
dart部分声明
typedef reverse_func = Pointer Function(Pointer str, Int32 length);
typedef Reverse = Pointer Function(Pointer str, int length);
调用
final reversePointer = dylib.lookup>('reverse');
final reverse = reversePointer.asFunction();
final reversedMessage = Utf8.fromUtf8(reverse(Utf8.toUtf8('backwards'), 9));
print('$reversedMessage');
注意官方示例的所有指针全部没有释放,Utf8.toUtf8(’’)还有dylib.lookup()方法都会调用allocate函数,这个就会让原生给你动态分配空间,会执行malloc函数,我也看到一些大佬的帖子提到不释放是会内存泄漏的
OK这篇结束,任何问题欢迎评论区留言,下一篇是传char **和原生遍历这个char **