0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙开发接口公共事件与通知:【FFI能力】 N-API在Android、iOS平台应用的使用指导

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-05-25 16:33 次阅读

N-APIAndroid平台应用的使用指导

N-API接口可以实现ArkTS/TS/JS与C/C++(Native)之间的交互,ArkUI-X中支持的N-API接口情况和使用场景请见[FFI能力(N-API)]。本文档以[ArkUI-X/Samples]中的Native样例工程为例,介绍如何在Android平台上使用N-API能力完成跨语言调用。

开发流程

使用N-API完成跨语言调用的整体开发流程分为以下五步,即

  1. 环境准备:获取Native工程。
  2. 提供Native能力:使用N-API接口提供Native能力,同时,Native接口中还可调用ArkTS/TS/JS传入的方法。
  3. 定义接口声明:添加Native侧暴露给ArkTS侧接口的声明。
  4. 调用Native能力:ArkTS/TS/JS侧使用Native能力。
  5. 编译与运行应用:编出应用,并在android平台上运行,观察效果。
  6. 开发前请熟悉鸿蒙开发指导文档 :[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]点击或者复制转到。

这里通过修改样例工程,提供两个例子来介绍N-API的典型使用场景:

  1. 提供一个名为Add的Native方法,ArkTS侧调用该方法并传入两个number,Native方法将这两个number相加并返回到ArkTS侧。以此介绍:ArkTS侧如何调用到Native侧方法。
  2. 提供一个名为NativeCallArkTS的Native方法,ArkTS侧调用该方法并传入一个ArkTS function,Native方法中调用这个ArkTS function,并将其结果返回ArkTS侧。以此介绍:Native侧如何调用到ArkTS侧方法。

1. 环境准备

从[ArkUI-X/Samples]中获取Native样例工程,并使用DevEco Studio(V4.0 Beta2及以上版本)打开该工程,打开后将自动进行工程初始化。

2. 提供Native能力

利用N-API接口,进行Native能力开发。同时,本样例中的NativeCallArkTS也完成了Native侧调ArkTS侧的演示。

// entrysrcmaincpphello.cpp,包含Native侧逻辑。
// 引入N-API相关头文件。
#include "napi/native_api.h"

// 开发者提供的Native方法,入参有且仅有如下两个,开发者不需进行变更。
// napi_env 为当前运行的上下文。
// napi_callback_info 记录了一些信息,包括从ArkTS侧传递过来参数等。
static napi_value Add(napi_env env, napi_callback_info info)
{
    // 期望从ArkTS侧获取的参数的数量,napi_value可理解为ArkTS value在Native方法中的表现形式。
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    
    // 从info中,拿到从ArkTS侧传递过来的参数,此处获取了两个ArkTS参数,即arg[0]和arg[1]。
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    // 获取arg[0]的类型
    napi_valuetype valuetype0 = napi_undefined;
    napi_typeof(env, args[0], &valuetype0);

    // 获取arg[1]的类型
    napi_valuetype valuetype1 = napi_undefined;
    napi_typeof(env, args[1], &valuetype1);

    // 校验入参类型,若不是number,则返回undefined
    if ((valuetype0 != napi_number) || (valuetype1 != napi_number)) {
        napi_value undefined = nullptr;
        napi_get_undefined(env, &undefined);
        return undefined;
    }

    // 将获取的ArkTS参数转换为Native信息,此处ArkTS侧传入了两个number,这里将其转换为Native侧可以操作的double类型。
    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);
    
    // Native侧的业务逻辑,这里简单以两数相加为例。
    double nativeSum = value0 + value1;
    
    // 此处将Native侧业务逻辑处理结果转换为ArkTS值,并返回给ArkTS。
    napi_value sum = nullptr;
    napi_create_double(env, nativeSum , &sum);
    return sum;
}

static napi_value NativeCallArkTS(napi_env env, napi_callback_info info)
{
    // 期望从ArkTS侧获取的参数的数量,napi_value可理解为ArkTS value在Native方法中的表现形式。
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    // 从info中,拿到从ArkTS侧传递过来的参数,此处获取了一个ArkTS参数,即arg[0]。
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    // 获取arg[0]的类型
    napi_valuetype valuetype = napi_undefined;
    napi_typeof(env, args[0], &valuetype);

    // 校验入参类型
    if (valuetype != napi_function) {
        napi_value undefined = nullptr;
        napi_get_undefined(env, &undefined);
        return undefined;
    }
    
    // 创建一个ArkTS string作为ArkTS function的入参。
    napi_value argv = nullptr;
    // 创建一个js string
    napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &argv);
    
    napi_value result = nullptr;
    // Native方法中调用ArkTS function,其返回值保存到result中并返到ArkTS侧。
    napi_call_function(env, nullptr, args[0], 1, &argv, &result);
    
    return result;
}

EXTERN_C_START
// Init将在exports上挂上Add/NativeCallArkTS这些Native方法,此处的exports就是开发者import之后获取到的ArkTS对象。
static napi_value Init(napi_env env, napi_value exports)
{
    // 函数描述结构体,以Add为例,第三个参数"Add"为上述的Native方法,
    // 第一个参数"add"为ArkTS侧对应方法的名称。
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr },
    };
    // 在exports这个ArkTS对象上,挂载Native方法。
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
    .nm_version =1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

// 打开so时,该函数将自动被调用,使用上述demoModule模块信息,进行模块注册相关动作。
extern "C" __attribute__((constructor)) void RegisterHelloModule(void)
{
    napi_module_register(&demoModule);
}

3. 定义接口声明

d.ts文件中添加暴露接口声明

// entrysrcmaincpptypeslibentryindex.d.ts,包含Native侧暴露给ArkTS侧接口的声明。
// Native侧暴露给ArkTS侧接口的声明。
export const add: (a: number, b: number) = > number;
export const nativeCallArkTS: (a: object) = > string;

4. 调用Native能力

在ArkTS/TS/JS侧使用Native能力。

  1. entry.add接口完成了ArkTS -> Native的调用。
  2. entry.nativeCallArkTS接口完成了ArkTS -> Native -> ArkTS的调用。
// entrysrcmainetspagesIndex.ets,包含ArkTS侧逻辑。
// 通过import的方式,引入Native能力。
import entry from 'libentry.so'

function TestFunction(str) {
  let ret = str + ' world!'
  return ret;
}

@Entry
@Component
struct Index {
  @State message: string = 'Test native api: 2 + 3 = ?'
  @State message2: string = 'Click me'

  build() {
    Row() {
      Column() {
        // 第一个按钮,调用add方法,对应到Native侧的Add方法,进行两数相加。
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() = > {
            this.message = "Test native api: 2 + 3 = " + entry.add(2, 3);
            console.log("Test NAPI 2 + 3 = " + entry.add(2, 3));
          })
        // 第二个按钮,调用nativeCallArkTS方法,对应到Native的NativeCallArkTS,在Native中执行ArkTS function。
        Text(this.message2)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() = > {
            this.message2 = entry.nativeCallArkTS(TestFunction);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

5. 编译与运行应用

点击DevEco Studio(V4.0 Beta2及以上版本)中的"Build > Build Hap(s)/APP(s) > Build APP(s)"进行编译,并利用Android Studio打开Native工程的".arkui-xandroid"文件夹,点击"Run app"按钮后运行应用。应用运行后,点击页面上的两个文本,文本做出相应变换。 运行效果

开发建议

注册建议

  • nm_register_func对应的函数(如上述Init函数)需要加上static,防止与其他so里的符号冲突。
  • 模块注册的入口,即使用__attribute__((constructor))修饰的函数的函数名(如上述RegisterHelloModule函数)需要确保不与其他模块重复。

so命名规则

so命名必须符合以下规则:

  • 每个模块对应一个so。
  • 如模块名为entry,则so的名字为libentry.sonapi_modulenm_modname字段应为entry,大小写与模块名保持一致,应用使用时写作:import entry from 'libentry.so'

JS对象线程限制

ArkCompiler会对JS对象线程进行保护,使用不当会引起应用crash,因此需要遵循如下原则:

  • N-API接口只能在JS线程使用。
  • Native接口入参的env与线程绑定,不能跨线程使用。Native侧JS对象只能在创建时的线程使用,即与线程所持有的env绑定。

头文件引入限制

在引入头文件时,需引入"napi/native_api.h",否则会出现N-API接口无法找到的编译报错。

HarmonyOSOpenHarmony鸿蒙文档籽料:mau123789是v直接拿

搜狗高速浏览器截图20240326151450.png

N-API在iOS平台应用的使用指导

N-API接口可以实现ArkTS/TS/JS与C/C++(Native)之间的交互,ArkUI-X中支持的N-API接口情况和使用场景请见[FFI能力(N-API)]。N-API在iOS平台上的使用方式中,除去"编译与运行应用"环节与Android平台略有差异外,其余部分均与Android平台相同。本文档将主要介绍N-API在iOS平台上的"编译与运行应用"环节,其他环节可参考[N-API在Android平台应用的使用指导]。

编译与运行应用

相较于Android平台,iOS平台上编译/运行Native工程时需要进行签名操作,完整步骤如下:

  1. 点击DevEco Studio(V4.0 Beta2及以上版本)中的"Build > Build Hap(s)/APP(s) > Build APP(s)"进行编译。
  2. 使用Xcode工具打开".arkui-xios"文件,在Xcode中完成签名操作。 签名
  3. 点击Xcode中的运行按钮以完成二次打包,运行应用。 运行结果
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 移动开发
    +关注

    关注

    0

    文章

    52

    浏览量

    9686
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2634

    浏览量

    66203
  • HarmonyOS
    +关注

    关注

    79

    文章

    1967

    浏览量

    30001
  • OpenHarmony
    +关注

    关注

    25

    文章

    3657

    浏览量

    16129
  • 鸿蒙OS
    +关注

    关注

    0

    文章

    188

    浏览量

    4367
收藏 人收藏

    评论

    相关推荐

    AndroidiOS鸿蒙平台框架ArkUI-X

    AndroidiOS鸿蒙多个平台上提供生动而流畅的用户界面体验。 一、配套关系 表1 版本软件和平台配套关系 目标
    的头像 发表于 01-31 14:52 1718次阅读
    跨<b class='flag-5'>Android</b>、<b class='flag-5'>iOS</b>、<b class='flag-5'>鸿蒙</b>多<b class='flag-5'>平台</b>框架ArkUI-X

    鸿蒙开发接口公共事件与通知:【@ohos.commonEvent (公共事件模块)】

    本模块首批接口API version 7开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。
    的头像 发表于 05-21 11:13 1020次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>开发</b><b class='flag-5'>接口</b><b class='flag-5'>公共事</b>件与<b class='flag-5'>通知</b>:【@ohos.commonEvent (<b class='flag-5'>公共事</b>件模块)】

    鸿蒙开发接口公共事件与通知:【@ohos.events.emitter (Emitter)】

    本模块首批接口API version 7开始支持。
    的头像 发表于 05-21 16:06 1280次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>开发</b><b class='flag-5'>接口</b><b class='flag-5'>公共事</b>件与<b class='flag-5'>通知</b>:【@ohos.events.emitter (Emitter)】

    鸿蒙开发接口公共事件与通知:【Notification模块】

    本模块首批接口API version 7开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。
    的头像 发表于 05-21 17:04 2004次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>开发</b><b class='flag-5'>接口</b><b class='flag-5'>公共事</b>件与<b class='flag-5'>通知</b>:【Notification模块】

    鸿蒙ArkUI-X跨语言调用说明:【平台桥接开发指南(Android)Bridge API

    本模块提供ArkUI端和AndroidiOS平台端消息通信的功能,包括数据传输、方法调用和事件调用。需配套平台API使用,
    的头像 发表于 05-25 16:30 600次阅读
    <b class='flag-5'>鸿蒙</b>ArkUI-X跨语言调用说明:【<b class='flag-5'>平台</b>桥接<b class='flag-5'>开发</b>指南(<b class='flag-5'>Android</b>)Bridge <b class='flag-5'>API</b>】

    鸿蒙原生应用/元服务开发-通知添加行为意图

    WantAgent提供了封装行为意图的能力,这里所说的行为意图主要是指拉起指定的应用组件及发布公共事件等能力。HarmonyOS支持以通知的形式,将WantAgent从发布方传递至接收
    发表于 01-05 15:07

    鸿蒙实战开发-如何安全和高效的使用N-API开发Native模块

    基础能力的介绍,同时,方舟 ArkTS 运行时提供的 N-API 接口,封装了方舟引擎的能力功能上与 Node.js 社区保持一致,这里
    发表于 05-09 15:55

    基于ArkTS语言的OpenHarmony APP应用开发公共事件的订阅和发布

    /d05_CustomCommonEvent API接口:9 2、知识准备 2.1、commonEvent模块 OpenHarmony通过CES(Common Event Service,公共事件服务)为
    发表于 09-18 13:16

    HarmonyOS应用开发-公共事件处理

    开发过程中service想要控制多个ability时,可以考虑使用公共事件处理。发布无序的公共事件: //发布公共事件 同步修改卡片与页面
    发表于 11-02 15:15

    HarmonyOS-API7相对API6差异主要变更内容

    条件、循环渲染、懒加载渲染,支持自定义组件。JS新增5800+的API接口,覆盖多个能力模块:新增一系列事件通知能力接口,可实现普通
    发表于 02-15 14:35

    HarmonyOS/OpenHarmony应用开发-ArkTSAPI系统能力SystemCapability

    一、 系统能力API SysCap,全称SystemCapability,即系统能力,指操作系统中每一个相对独立的特性,如包管理、公共事件、通知
    发表于 08-10 16:39

    OpenHarmony 应用开发SDK、API 与基础工具

    ArkTS 对象的能力,使用类 Node 的 N-API 接口命名。开发者使用 C/C++开发业务,通过
    发表于 09-19 15:45

    Android开发”和“iOS开发”实力大碰撞

    iOSAndroid更有优势。虽然Android有自己的一些优势,但显然开发一款优秀的iOS应用比开发
    发表于 12-07 16:22 1376次阅读

    N-API的JS堆对象生命周期管理

    N-API是Node API的简写,同时也是nodejs的JS VM(链)接入原生模块.node文件的应用程序二进制接口(i.e. ABI)。借助N-API引入的抽象隔离,升级node
    的头像 发表于 12-29 09:41 494次阅读
    <b class='flag-5'>N-API</b>的JS堆对象生命周期管理

    鸿蒙开发接口公共事件与通知:【FFI能力(Node-API)】

    Node-API是封装底层JavaScript运行时能力的一套Native接口。OpenHarmony的N-API组件对Node-API
    的头像 发表于 05-21 16:38 908次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>开发</b><b class='flag-5'>接口</b><b class='flag-5'>公共事</b>件与<b class='flag-5'>通知</b>:【<b class='flag-5'>FFI</b><b class='flag-5'>能力</b>(Node-<b class='flag-5'>API</b>)】