连接到动态库

注意: 下面的示例主要针对的是使用Golang生成的动态库

生成动态库

使用C语言字符串参数

// 你需要导入C
import "C"

// 在需要导出的动态库上添加export注解,注意export前面没有空格,函数名开头大写

//export Connect
func Connect(s1 *C.char, s2 *C.char) *C.char {
	gs1 := C.GoString(s1)
	gs2 := C.GoString(s2)

	return C.CString(gs1 + " " + gs2)
}

使用整数或者浮点

C.int或者C.double等可以直接计算

//export Add
func Add(a C.int, b C.int) C.int {
  return a + b
}

// export Division
func Division(a C.double, b C.double) C.double {
	return a / b
}

返回struct或者JSON

//export GetData
func GetData(path *C.char) *C.char {
  // 假设getData返回的是一个自定义的struct
	data, _ := json.Marshal(getData(C.GoString(path)))
	return C.CString(string(data))
}

导出动态库

通过下面的命令导出动态库

#  macOS
go build -buildmode=c-shared -ldflags="-s -w" -o build/core.dylib
# Windows
go build -buildmode=c-shared -ldflags="-s -w" -o build/core.dll

将动态库打包到App内部

详细的说明见Flutter-FFI-Template

Windows

将动态库移动到windows目录下然后在windows/runner/CMakeLists.txt最后添加
注意替换两处dll文件名

set(DLL_SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../<动态库名称>.dll")

add_custom_command(
    TARGET ${BINARY_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        "${DLL_SOURCE_PATH}"
        "$<TARGET_FILE_DIR:${BINARY_NAME}>/<动态库名称>.dll"
    COMMENT "Copy dll file"
)

macOS

参考文档

iOS

参考文档

Android

参考文档

连接到动态库

将动态库存储为Dart函数

import 'dart:ffi';

// 定义类型,一个为动态库函数,一个为Dart函数,注意这两个类型完全不同
// 如果是返回字符串(指针)的函数则可以不区分
typedef AddDart = int Function(int, int);
typedef Add = Int Function(Int, Int);

// 如果是字符串可以这样只保留一个typedef
typedef CharSet=Pointer<Utf8> Function(Pointer<Utf8>);

// 绑定动态库
final dynamicLib=DynamicLibrary.open(Platform.isMacOS ? 'core.dylib' : 'core.dll');

// 绑定函数
final AddDart add=dynamicLib
.lookup<NativeFunction<Add>>('Add')
.asFunction();

// 如果是返回字符串的函数
final CharSet charSet=dynamicLib
.lookup<NativeFunction<CharSet>>('CharSet')
.asFunction();

运行函数

短时间调用

// 如果不含字符串可以直接调用
final newNum=add(1, 2);

// 如果含有字符串
// 注意传递的参数和返回值都需要转换
// 注意释放指针

final charPtr="Hello World!!!".toNativeUtf8();
final String data=charSet(charPtr).toDartString();
malloc.free(charPtr);

await调用

可以使用await异步运行

static void isolateFunction(List params){
  final dynamicLib=DynamicLibrary.open(Platform.isMacOS ? 'core.dylib' : 'core.dll');
  final AddDart add=dynamicLib
  .lookup<NativeFunction<Add>>('Add')
  .asFunction();

  add(params[0], params[1]);
}

await compute(isolateFunction, [param1, param2]);

持续运行

防止执行过程中程序卡死

static void isolateFunction(List params){
  final dynamicLib=DynamicLibrary.open(Platform.isMacOS ? 'lib.dylib' : 'lib.dll');
  final AddDart add=dynamicLib
  .lookup<NativeFunction<Add>>('Add')
  .asFunction();

  add(params[0], params[1]);
}

// 注意这里如果使用compute会导致一直等待isolateFunction结束运行
await Isolate.spawn(isolateFunction, [param1, param2]);

字符串作为参数

Warning

如果在isolate或者compute中使用.toNativeUtf8(),对这个参数进行断言

static void isolateFunction(List params){
  final dynamicLib=DynamicLibrary.open(Platform.isMacOS ? 'core.dylib' : 'core.dll');
  final ConcatDart add=dynamicLib
  .lookup<NativeFunction<Concat>>('Concat')
  .asFunction();

  // 注意在这里断言类型
  final param1Ptr = (params[0] as String).toNativeUtf8();
  final param2Ptr = (params[1] as String).toNativeUtf8();

  add(param1Ptr, param2Ptr);

  // 注意释放指针
  malloc.free(param1Ptr);
  malloc.free(param2Ptr);
}

await compute(isolateFunction, [param1, param2]);