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);
}
複製代碼