Flutter动态化框架Kraken中C同步调用Dart实现原理

Flutter中使用dart:ffi Dart同步调用C是官方已经支持的。可是C调用Dart官方并无说怎么实现,网上也找不到相关的实现。 听说阿里已经实现了C调用Dart的方法,而且已经申请了专利,在Kraken项目中开源了。火烧眉毛的看了下源码。下面进行总结下。数组

首先看注册方法(Dart层),整体来讲就是把要暴露的Dart方法地址经过ffi传给C Pointer是本机C内存的指针, _dartNativeMethods是一个数组,用于存放dart 方法的地址markdown

void registerDartMethodsToCpp() {
  Pointer<Uint64> bytes = allocate<Uint64>(count: _dartNativeMethods.length);
  Uint64List nativeMethodList = bytes.asTypedList(_dartNativeMethods.length);
  nativeMethodList.setAll(0, _dartNativeMethods);
  _registerDartMethods(bytes, _dartNativeMethods.length);
}
复制代码

那么如何获取Dart方法地址的呢?使用Pointer.fromFunction能够将Dart方法转为C方法指针,而后使用_nativeInvokeModule.address就能够获取到Dart方法地址了。函数

final Pointer<NativeFunction<Native_InvokeModule>> _nativeInvokeModule = Pointer.fromFunction(_invokeModule);
复制代码

而后看下Dart层的注册方法,lookup是dart:ffi暴露的方法用于调用C层代码,能够看到调用了C层的registerDartMethods(Pointer methodBytes, Int32 length);ui

final Dart_RegisterDartMethods _registerDartMethods =
    nativeDynamicLibrary.lookup<NativeFunction<Native_RegisterDartMethods>>('registerDartMethods').asFunction();
    
typedef Native_RegisterDartMethods = Void Function(Pointer<Uint64> methodBytes, Int32 length);
复制代码

下面是C层的代码,使用reinterpret_cast将Dart层的函数地址转为函数指针编码

void registerDartMethods(uint64_t *methodBytes, int32_t length) {
  size_t i = 0;

  methodPointer->invokeModule = reinterpret_cast<InvokeModule>(methodBytes[i++]);
  ......
}

typedef NativeString *(*InvokeModule)(void *callbackContext, int32_t contextId, NativeString *moduleName, NativeString *method, NativeString *params, AsyncModuleCallback callback);
复制代码

最后就能够在C层调用Dart方法了~spa

NativeString *response = getDartMethod()->invokeModule(bridgeContext, contextId, moduleName, method, params,
                                                               handleInvokeModuleTransientCallback);
复制代码

这里有一点要注意的是,Dart方法必须在Flutter UI线程调用,不然会Crash线程

还有一些要补充的 String类型须要转换下Dart是UTF-16,C是UTF-8,Dart String转C String转换方法 Utf8.toUtf8 Dart String和JS String都是UTF-16 ,转换方法以下,官方宣称编码格式都是UTF-16没有转换成本指针

class NativeString extends Struct {
  Pointer<Uint16> string;

  @Int32()
  int length;
}

String uint16ToString(Pointer<Uint16> pointer, int length) {
  return String.fromCharCodes(pointer.asTypedList(length));
}

Pointer<Uint16> _stringToUint16(String string) {
  final units = string.codeUnits;
  final Pointer<Uint16> result = allocate<Uint16>(count: units.length);
  final Uint16List nativeString = result.asTypedList(units.length);
  nativeString.setAll(0, units);
  return result;
}

Pointer<NativeString> stringToNativeString(String string) {
  assert(string != null);
  Pointer<NativeString> nativeString = allocate<NativeString>();
  nativeString.ref.string = _stringToUint16(string);
  nativeString.ref.length = string.length;
  return nativeString;
}

String nativeStringToString(Pointer<NativeString> pointer) {
  return uint16ToString(pointer.ref.string, pointer.ref.length);
}
复制代码
相关文章
相关标签/搜索