HarmonyOS NEXT应用开发之NAPI封装ArkTS接口案例_harmonyos napi(1)

avatar
作者
筋斗云
阅读量:0
介绍

部分应用的主要开发语言为C/C++,但是HarmonyOS的部分接口仅以ArkTS的形式暴露,因此需要将ArkTS的接口封装为Native接口。本例以DocumentViewPicker的Select方法为例,提供了Napi封装ArkTS API的通用方法,本例包含内容如下:

  1. Native侧与ArkTS侧的相互调用
  2. Native对象转换为ArkTS对象(包括如何在ArkTS侧调用一般形式的Native方法)
  3. 线程安全函数的使用
  4. 等待线程安全函数的执行结果
  5. 多实例情况下,如何在正确的窗口内执行方法
效果图预览

测试说明

  1. 点击"JS线程调用"按钮,从native侧js线程拉起picker
  2. 点击"PThread线程调用"按钮,从native侧pthread子线程中拉起picker
  3. 拉起picker后,单击直接选择单个文件,长按可选择多个文件
  4. 本例在拉起picker时设置了maxSelectNumber=3,最多个选择3个文件

集成说明

  1. 源码集成
  2. 参考aki方式集成

使用说明

  1. [建议]在EntryAbility之外调用registryDocumentViewPickerFn方法,避免多实例下的重复注册
  2. [必须]给每个UIAbility生成唯一的ID属性,可使用generateAbilityID方法
  3. [必须]在UIAbility的onWindowStageCreate中的windowStage.loadContent的回调中调用addUIContext方法
  4. [必须]在UIAbility的onWindowStageCreate中的windowStage.loadContent之后调用setTopAbilityID
  5. [建议]在UIAbility的onWindowStageDestroy中调用removeUIContext方法
实现思路
  1. native侧需要主动调用ets侧的代码,需要将ets侧代码注入到native侧,并在注册时创建函数的引用及线程安全函数,并保存当前线程(js线程)的id及env供后续调用时使用

ets侧:

etswrapper.registryDocumentViewPickerFn(documentViewPickerSelect, documentViewPickerSave);

native侧:

NODE_API_CALL(env, napi_create_threadsafe_function(env, args[0], nullptr, selectWork, 0, 1, nullptr, nullptr,
nullptr, tsfn::callJSSelect, &(uniContext->selectTsfn)));
NODE_API_CALL(env, napi_create_threadsafe_function(env, args[1], nullptr, saveWork, 0, 1, nullptr, nullptr,
nullptr, tsfn::callJSSave, &(uniContext->saveTsfn)));
NODE_API_CALL(env, napi_create_reference(env, args[0], 1, &(uniContext->selectRef)));
NODE_API_CALL(env, napi_create_reference(env, args[1], 1, &(uniContext->saveRef)));
uniContext->pickerEnv = env;
uniContext->jsThreadID = fns::getCurrentThreadID();

  1. 对于某些有返回值的ets函数,为了获取其返回值,需要对调用线程进行区分:如果是js线程,直接通过napi_call_function发起调用并获得返回值;否则需要等异步线程执行完毕并通过全局变量获取结果然后返回

if (uniContext->jsThreadID != fns::getCurrentThreadID()) {
status = napi_acquire_threadsafe_function(uniContext->selectTsfn);
status = napi_call_threadsafe_function(uniContext->selectTsfn, selectParam, napi_tsfn_blocking);
std::unique_lockstd::mutex unil(uniContext->resultWaitUtil.lock);
uniContext->resultWaitUtil.cv.wait(unil, [] { return uniContext->resultWaitUtil.isFinished; });
return;
} else {
status = napi_call_function(uniContext->pickerEnv, nullptr, tsSelect, 4, args, &result);
}

  1. 因为napi中的线程安全函数只能通过napi_threadsafe_function_call_js中的data参数进行传参,因此需要将所需参数全部封装到一个对象中

DocumentViewPickerSelectParam *selectParam = new DocumentViewPickerSelectParam(options, thenWrapper, catchWrapper)

  1. 因为本例中的filepicker是异步的,回调函数需要调用者传入,而napi中若需要将native方法直接封装为ets方法对于函数类型是有要求的。因此这里通过将回调函数封装到对象中,通过对象包装来实现将一般类型的函数封装为ets侧的函数:

native侧:

// step:类型声明
class DocumentViewPickerSelectThenCbWrapper {
public:
documentSelectThenFn thenFn;
DocumentViewPickerSelectThenCbWrapper(documentSelectThenFn fn) : thenFn(fn) {}
/**

  • 将对象实例包装为js对象
    /
    napi_value convert2NapiValue(napi_env env);
    /
    *
  • 参数是string[],这里面会调用thenFn,ets侧调用
    */
    static napi_value call(napi_env env, napi_callback_info info);
    };
    // step2:convert2NapiValue方法实现
    napi_value etswrapper::DocumentViewPickerSelectThenCbWrapper::convert2NapiValue(napi_env env) {
    napi_value object;
    DocumentViewPickerSelectThenCbWrapper *thenWrapper = new DocumentViewPickerSelectThenCbWrapper(this->thenFn);
    NODE_API_CALL(env, napi_create_object(env, &object));
    NODE_API_CALL(env, napi_wrap(
    env, object, thenWrapper,
    [](napi_env env, void *finalize_data, void *finalize_hint) -> void {
    delete reinterpret_cast<DocumentViewPickerSelectThenCbWrapper *>(finalize_data);
    },
    nullptr, nullptr));
    napi_property_descriptor desc[] = {
    {“call”, nullptr, DocumentViewPickerSelectThenCbWrapper::call, nullptr, nullptr, nullptr, napi_default,
    nullptr},
    };
    NODE_API_CALL(env, napi_define_properties(env, object, sizeof(desc) / sizeof(*desc), desc));
    return object;
    }
    // step3:call方法实现
    napi_value etswrapper::DocumentViewPickerSelectThenCbWrapper::call(napi_env env, napi_callback_info info) {
    // …
    napi_value thisArg;
    NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
    DocumentViewPickerSelectThenCbWrapper *thenWrapper;
    NODE_API_CALL(env, napi_unwrap(env, thisArg, reinterpret_cast<void **>(&thenWrapper)));
    // …
    thenWrapper->thenFn(data);
    return nullptr;
    }

ets侧:

function documentViewPickerSelect(uiContext: UIContext, options: picker.DocumentSelectOptions, thenWrapper:
StringArrayThenCbWrapper, catchWrapper: CatchCbWrapper): void {
let documentViewPicker: picker.DocumentViewPicker = new picker.DocumentViewPicker();
documentViewPicker.select(options).then((value: string[]) => {
thenWrapper.call(value);
}).catch((error: BusinessError) => {
// …

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)**
[外链图片转存中…(img-Jd7kgove-1712663975315)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!