From 1c4551933ccb89ebe339d88e6c6ae68b1fc120f2 Mon Sep 17 00:00:00 2001 From: zealotchen Date: Thu, 12 Dec 2024 21:46:50 +0800 Subject: [PATCH] feat(ohos): update jsi doc && fix null pointer --- docs/development/native-module.md | 135 ++++++++++++++++++ .../include/connector/arkts_turbo_module.h | 3 + .../impl/connector/src/arkts_turbo_module.cc | 37 ++++- .../modules/HippyModuleManagerImpl.ets | 2 + modules/ohos/oh_napi/include/oh_napi/ark_ts.h | 2 + .../oh_napi/include/oh_napi/oh_napi_object.h | 4 + modules/ohos/oh_napi/src/ark_ts.cc | 8 ++ modules/ohos/oh_napi/src/oh_napi_object.cc | 11 +- 8 files changed, 198 insertions(+), 4 deletions(-) diff --git a/docs/development/native-module.md b/docs/development/native-module.md index eddde6ffda1..1d388038e0e 100644 --- a/docs/development/native-module.md +++ b/docs/development/native-module.md @@ -260,6 +260,141 @@ HIPPY_EXPORT_METHOD(click) { @end ``` +# 鸿蒙 + +很多时候 `JS` 需要访问对应终端的一些能力模块,比如数据库、下载、网络请求等,这时候就需要使用 `Module` 来暴露接口给JS使用。Voltron SDK 中默认实现了部分 `Module`,但这极有可能无法满足你的需求,这就需要你对 `Module` 进行扩展封装。 + +--- + +## Module扩展 + +我们将以 `TestModule` 为例,从头扩展一个 `Module`,这个 `Module` 将展示前端如何调用终端能力,并且把结果返回给前端 + +终端扩展 `Module` 包括四步: + +1. 创建 `TestModule` +2. 实现导出给 `JS` 的方法。 +3. 注册 `Module`。 + +## 1. 创建 `TestModule`,并且继承 HippyNativeModuleBase + +```typescript +export class ExampleNativeModule extends HippyNativeModuleBase { + public static readonly NAME = 'ExampleNativeModule' + + constructor(ctx: HippyEngineContext) { + super(ctx) + } + + public call(method: string, params: Array, promise: HippyModulePromise): HippyAny { + switch (method) { + case 'test': { + this.test(); + break; + } + case 'testPromise': { + this.testPromise(params, promise); + break; + } + case 'testSendEvent': { + this.testSendEvent(params, promise); + } + default: + super.call(method, params, promise); + } + return null; + } + + public test() { + LogUtils.i(ExampleNativeModule.NAME, 'module test'); + } + + public testPromise(params: Array, promise: HippyModulePromise) { + promise.resolve('test'); + } + + public testSendEvent(params: Array, promise: HippyModulePromise) { + LogUtils.i(ExampleNativeModule.NAME, 'testSendEvent'); + if (this.ctx != null && this.ctx.getModuleManager() != null) { + const eventModule = this.ctx.getModuleManager().getJavaScriptModule(EventDispatcher.MODULE_NAME); + if (eventModule != null) { + const event = 'testEvent'; + const params = new Map(); + params.set('testString', 'testStringValue'); + + const valueMap = new Map(); + valueMap.set('testString2', 'testStringValue2'); + params.set('testMap', valueMap); + + const array: HippyArray = []; + array.push(valueMap); + params.set('testArray', array); + + (eventModule as EventDispatcher).receiveNativeEvent(event, params); + } + } + } + +} + +``` + +需要注意的是,这里与Android、iOS有几处不同。 + +1. 需要指定 NAME,设置为前端调用的 module name + +2. 需要实现 call 方法 + +## Turbo Module扩展 + +和 Module 的扩展一致,不过还需要配置 isTurbo 方法,且不需要实现 call 方法,参考如下: + +```typescript +export class ExampleNativeTurboModule extends HippyNativeModuleBase { + public static readonly NAME = 'demoTurbo' + + constructor(ctx: HippyEngineContext) { + super(ctx) + } + + isTurbo(): boolean { + return false + } + + public getString(info: string): string { + return 'demoTurbo' + info; + } + + public getNum(num: number): number { + return num; + } + + public getBoolean(b: boolean): boolean { + return b; + } + + public getMap(map: HippyMap): HippyMap { + return map + } + + public getArray(array: HippyArray): HippyArray { + return array + } + + public getObject(obj: HippyAny): HippyAny { + return obj + } + + public getTurboConfig(): TurboConfig { + return new TurboConfig(); + } + + public printTurboConfig(turboConfig: TurboConfig): string { + return turboConfig.info; + } +} + +``` # Voltron diff --git a/framework/ohos/src/main/cpp/impl/connector/include/connector/arkts_turbo_module.h b/framework/ohos/src/main/cpp/impl/connector/include/connector/arkts_turbo_module.h index 8f0bdf35af8..f3ad8455771 100644 --- a/framework/ohos/src/main/cpp/impl/connector/include/connector/arkts_turbo_module.h +++ b/framework/ohos/src/main/cpp/impl/connector/include/connector/arkts_turbo_module.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include "connector/turbo.h" #include "driver/napi/js_ctx.h" @@ -63,6 +64,7 @@ class ArkTsTurboModule { std::shared_ptr impl; napi_env env; std::unique_ptr wrapper_holder_; + std::set method_set_; std::shared_ptr constructor; std::unique_ptr constructor_wrapper; @@ -74,6 +76,7 @@ class ArkTsTurboModule { const std::shared_ptr& prop_name, hippy::napi::CallbackInfo& info, void* data); + void InitMethodSet(); }; diff --git a/framework/ohos/src/main/cpp/impl/connector/src/arkts_turbo_module.cc b/framework/ohos/src/main/cpp/impl/connector/src/arkts_turbo_module.cc index 54c1ca5bec3..6f4b5683d1c 100644 --- a/framework/ohos/src/main/cpp/impl/connector/src/arkts_turbo_module.cc +++ b/framework/ohos/src/main/cpp/impl/connector/src/arkts_turbo_module.cc @@ -57,19 +57,27 @@ std::shared_ptr ArkTsTurboModule::InvokeArkTsMethod(const std::shared_ auto context = scope->GetContext(); string_view str_view; std::string method; - std::shared_ptr result; + std::shared_ptr result = context->CreateUndefined(); if (context->GetValueString(prop_name, &str_view)) { method = StringViewUtils::ToStdString( StringViewUtils::ConvertEncoding(str_view, string_view::Encoding::Utf8).utf8_value()); } FOOTSTONE_DLOG(INFO) << "invokeArkTSMethod, method = " << method.c_str(); - + auto method_set = method_set_; OhNapiTaskRunner *taskRunner = OhNapiTaskRunner::Instance(env); - taskRunner->RunSyncTask([env = env, impl = impl, &info, context, method, &result]() { + taskRunner->RunSyncTask([env = env, impl = impl, &info, context, method, &result, &method_set]() { ArkTS arkTs(env); napi_ref turbo_module_ref = impl->GetRef(); auto turboModule = arkTs.GetObject(turbo_module_ref); + if (method_set.size() <= 0) { + FOOTSTONE_DLOG(ERROR) << "turboModule object has no method, method = " << method.c_str(); + return; + } + if (method_set.find(method) == method_set.end()) { + FOOTSTONE_DLOG(ERROR) << "turboModule method is null, method = " << method.c_str(); + return; + } std::vector args; for (size_t i = 0; i < info.Length(); ++i) { auto item = info[i]; @@ -82,11 +90,34 @@ std::shared_ptr ArkTsTurboModule::InvokeArkTsMethod(const std::shared_ return result; } +void ArkTsTurboModule::InitMethodSet() { + ArkTS arkTs(env); + napi_ref turbo_module_ref = impl->GetRef(); + auto turboModule = arkTs.GetObject(turbo_module_ref); + if (turboModule.isNull()) { + FOOTSTONE_DLOG(ERROR) << "turboModule object is null"; + return; + } + std::vector> value = turboModule.GetKeyValuePairs(); + if (value.size() <= 0) { + FOOTSTONE_DLOG(ERROR) << "turboModule object is null"; + return; + } + std::vector> pairs = turboModule.GetObjectPrototypeProperties(); + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto &pair = *it; + auto &pairItem1 = pair.first; + auto method = arkTs.GetString(pairItem1); + method_set_.insert(method); + } +} + ArkTsTurboModule::ArkTsTurboModule(const std::string& name, std::shared_ptr& impl, const std::shared_ptr& ctx, napi_env env) : name(name), impl(impl), env(env) { + InitMethodSet(); auto getter = std::make_unique([](CallbackInfo& info, void* data) { auto scope_wrapper = reinterpret_cast(std::any_cast(info.GetSlot())); auto scope = scope_wrapper->scope.lock(); diff --git a/framework/ohos/src/main/ets/hippy_framework/modules/HippyModuleManagerImpl.ets b/framework/ohos/src/main/ets/hippy_framework/modules/HippyModuleManagerImpl.ets index f99774ca55e..050684f1b25 100644 --- a/framework/ohos/src/main/ets/hippy_framework/modules/HippyModuleManagerImpl.ets +++ b/framework/ohos/src/main/ets/hippy_framework/modules/HippyModuleManagerImpl.ets @@ -150,11 +150,13 @@ export class HippyModuleManagerImpl implements HippyModuleManager { if (creator) { let module = creator(this.ctx) if (module.isTurbo() !== isTurbo) { + LogUtils.e('hippy', name + ' module is not turbo module') return null; } this.cachedNativeModuleMap.set(name, module) return module } else { + LogUtils.e('hippy', name + ' module is null') return null } } diff --git a/modules/ohos/oh_napi/include/oh_napi/ark_ts.h b/modules/ohos/oh_napi/include/oh_napi/ark_ts.h index 3936f195550..95be9aa45a1 100644 --- a/modules/ohos/oh_napi/include/oh_napi/ark_ts.h +++ b/modules/ohos/oh_napi/include/oh_napi/ark_ts.h @@ -117,6 +117,8 @@ class ArkTS { uint32_t GetArrayLength(napi_value array); std::vector> GetObjectProperties(napi_value object); + + std::vector> GetObjectPrototypeProperties(napi_value object); std::string GetString(napi_value value); diff --git a/modules/ohos/oh_napi/include/oh_napi/oh_napi_object.h b/modules/ohos/oh_napi/include/oh_napi/oh_napi_object.h index 185d34d2c0b..7ab3e978aaa 100644 --- a/modules/ohos/oh_napi/include/oh_napi/oh_napi_object.h +++ b/modules/ohos/oh_napi/include/oh_napi/oh_napi_object.h @@ -47,6 +47,10 @@ class OhNapiObject { napi_value GetProperty(napi_value key); std::vector> GetKeyValuePairs(); + + std::vector> GetObjectPrototypeProperties(); + + bool isNull(); private: ArkTS arkTs_; diff --git a/modules/ohos/oh_napi/src/ark_ts.cc b/modules/ohos/oh_napi/src/ark_ts.cc index e396dbb65ee..286e4627de4 100644 --- a/modules/ohos/oh_napi/src/ark_ts.cc +++ b/modules/ohos/oh_napi/src/ark_ts.cc @@ -254,6 +254,14 @@ uint32_t ArkTS::GetArrayLength(napi_value array) { return length; } +std::vector> ArkTS::GetObjectPrototypeProperties(napi_value object) { + napi_value prototype; + auto status = napi_get_prototype(env_, object, &prototype); + this->MaybeThrowFromStatus(status, "Failed to retrieve prototype object"); + auto result = GetObjectProperties(prototype); + return result; +} + std::vector> ArkTS::GetObjectProperties(napi_value object) { napi_value propertyNames; auto status = napi_get_property_names(env_, object, &propertyNames); diff --git a/modules/ohos/oh_napi/src/oh_napi_object.cc b/modules/ohos/oh_napi/src/oh_napi_object.cc index d5288d5ea01..ddbe6fc8e40 100644 --- a/modules/ohos/oh_napi/src/oh_napi_object.cc +++ b/modules/ohos/oh_napi/src/oh_napi_object.cc @@ -35,4 +35,13 @@ napi_value OhNapiObject::GetProperty(napi_value key) { std::vector> OhNapiObject::GetKeyValuePairs() { return arkTs_.GetObjectProperties(object_); -} \ No newline at end of file +} + +std::vector> OhNapiObject::GetObjectPrototypeProperties() { + return arkTs_.GetObjectPrototypeProperties(object_); +} + +bool OhNapiObject::isNull() { + auto type = arkTs_.GetType(object_); + return type == napi_undefined || type == napi_null; +}