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);
}
複製代碼
相關文章
相關標籤/搜索