三方库移植之NAPI开发[2]C/C++与JS的数据类型转

通过NAPI框架进行C/C++与JS数据类型的转换

  • OpenHarmony NAPI将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,统一封装成napi_value类型,下文中表述为JS类型,用于接收ArkUI应用传递过来的数据及返回数据给ArkUI应用。

ECMAScript是一种由Ecma国际(通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它可以理解为是JavaScript的一个标准,但实际上后两者是ECMA-262标准的实现和扩展。

  • 下面通过扩展一个简单的接口——Add(num1, num2)讲述具体细节,接口使用同步方式实现,NAPI的同步方式调用的扩展API代码处理流程如下图。

.cpp源码实现

  • 在《三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI 》一文的基础上修改hellonapi.cpp文件,其余文件不变。
  • hellonapi.cpp内容如下:
#include <string.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

//NAPI定义API方法(接口业务实现)时的接收参数为(napi_env, napi_callback_info),
static napi_value Add(napi_env env, napi_callback_info info) {

    //获取2个参数,值的类型是js类型(napi_value)
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    //NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    //获取并判断js参数类型
    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, NULL, "Wrong arguments. 2 numbers are expected.");
    return NULL;
  }

    //将js类型(napi_value)的参数值转换成C++类型double
    double value0;
    NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));

    double value1;
    NAPI_CALL(env, napi_get_value_double(env, args[1], &value1));

    //将结果由C++类型(double)转换成js类型(napi_value)
    //NAPI提供了一些方法以便将C/C++不同类型的值转为node_value类型,返回给JS代码。
    napi_value sum;
    NAPI_CALL(env, napi_create_double(env, value0 + value1, &sum));

   //返回napi_value类型结果
    return sum;
}

// napi_addon_register_func
//2.指定模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
static napi_value registerFunc(napi_env env, napi_value exports)
{

    // 在napi_property_descriptor desc[]中将编写C的“Add方法与对外暴露Js的接口“add”方法进行关联
    static napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;   
}

// 1.先定义napi_module,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模块对外接口注册函数
    .nm_modname = "hellonapi",  // 自定义模块名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

//3.模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

.cpp源码解析

注册NAPI模块、添加接口声明

// napi_addon_register_func
//2.指定模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
static napi_value registerFunc(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;   
}

// 1.先定义napi_module,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模块对外接口注册函数
    .nm_modname = "hellonapi",  // 自定义模块名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

//3.模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

接口业务实现C/C++代码

//NAPI定义API方法(接口业务实现)时的接收参数为(napi_env, napi_callback_info),
//其中napi_callback_info为上下文的信息
static napi_value Add(napi_env env, napi_callback_info info) {

    //获取2个参数,值的类型是js类型(napi_value)
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    //NAPI框架提供了napi_typeof方法用于获取指定js参数类型
    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, NULL, "Wrong arguments. 2 numbers are expected.");
    return NULL;
  }

    //将js类型(napi_value)的参数值转换成C++类型double
    double value0;
    NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));

    double value1;
    NAPI_CALL(env, napi_get_value_double(env, args[1], &value1));

    //将结果由C++类型(double)转换成js类型(napi_value)
    napi_value sum;
    NAPI_CALL(env, napi_create_double(env, value0 + value1, &sum));

   //返回napi_value类型结果
    return sum;
}
获取参数
static napi_value Add(napi_env env, napi_callback_info info) {
......
}
  • NAPI定义API方法时的接收参数为(napi_env, napi_callback_info)

    • 其中napi_callback_info为上下文的信息。
  • NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。

napi_get_cb_info函数在ohos3.2beta3源码foundation/arkui/napi/native_engine/native_api.cpp中

// Methods to work with napi_callbacks
// Gets all callback info in a single call. (Ugly, but faster.)
NAPI_EXTERN napi_status napi_get_cb_info(napi_env env,              // [in] NAPI environment handle
                                         napi_callback_info cbinfo, // [in] Opaque callback-info handle
                                         size_t* argc,         // [in-out] Specifies the size of the provided argv array
                                                               // and receives the actual count of args.
                                         napi_value* argv,     // [out] Array of values
                                         napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
                                         void** data)          // [out] Receives the data pointer for the callback.
{
    CHECK_ENV(env);
    CHECK_ARG(env, cbinfo);

    auto info = reinterpret_cast<NativeCallbackInfo*>(cbinfo);

    if ((argc != nullptr) && (argv != nullptr)) {
        size_t i = 0;
        for (i = 0; (i < *argc) && (i < info->argc); i++) {
            argv[i] = reinterpret_cast<napi_value>(info->argv[i]);
        }
        *argc = i;
    }

    if (argc != nullptr) {
        *argc = info->argc;
    }

    if (this_arg != nullptr) {
        *this_arg = reinterpret_cast<napi_value>(info->thisVar);
    }

    if (data != nullptr && info->functionInfo != nullptr) {
        *data = info->functionInfo->data;
    }

    return napi_clear_last_error(env);
}

napi_get_cb_info函数说明如下:

napi_status napi_get_cb_info(napi_env env,              
                             napi_callback_info cbinfo, 
                             size_t* argc,                          
                             napi_value* argv,     
                             napi_value* this_arg, 
                             void** data)         
  • 参数说明:

    • [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
    • [in] cbinfo: napi_callback_info对象,上下文的信息
    • [in-out] argc: argv数组的长度。若napi_callback_info中实际包含的参数的个数大于请求的数量argc,将只复制argc的值所指定数量的参数只argv中。若实际的参数个数小于请求的数量,将复制全部的参数,数组多余的空间用空值填充,并将参数实际长度写入argc。
    • [out] argv: 用于接收参数列表
    • [out] this_arg: 用于接收this对象
    • [out] data: NAPI的上下文数据 返回值:返回napi_ok表示转换成功,其他值失败。下面的返回napi_status方法一样。
  • 在Add方法中,调用napi_get_cb_info函数:

// env、info 参数由NAPI框架传入
static napi_value Add(napi_env env, napi_callback_info info) {
size_t requireArgc = 2;
  size_t argc = 2;
  napi_value args[2] = {nullptr};
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
  napi_value sum;
  return sum;
}

JS类型值转换为C/C++类型的值
  • 此示例中传入的参数是Javascript值类型,被NAPI框架封装成统一的唯一类型——napi_value类型,为了能够进行计算,我们需要获取其对应在C/C++中的类型的值。
    • NAPI提供了包括以下方法以便获取不同类型的值(ohos3.2beta3源码foundation/arkui/napi/native_engine/native_api.cpp中)
      • napi_get_value_double
      • napi_get_value_int32
      • napi_get_value_uint32
      • napi_get_value_int64
      • napi_get_value_bool
      • napi_get_value_string_latin1(Copies LATIN-1 encoded bytes from a string into a buffer)
      • napi_get_value_string_utf8(Copies UTF-8 encoded bytes from a string into a buffer)
      • napi_get_value_string_utf16
      • napi_get_value_external
      • napi_get_value_bigint_int64
      • napi_get_value_bigint_uint64
      • napi_get_value_bigint_words
    • 此示例hellonapi.cpp中使用到了napi_get_value_double方法,函数定义如下:

NAPI_EXTERN napi_status napi_get_value_double(napi_env env, napi_value value, double* result)
{
    CHECK_ENV(env);
    CHECK_ARG(env, value);
    CHECK_ARG(env, result);

    auto nativeValue = reinterpret_cast<NativeValue*>(value);

    RETURN_STATUS_IF_FALSE(env, nativeValue->TypeOf() == NATIVE_NUMBER, napi_number_expected);

    *result = *reinterpret_cast<NativeNumber*>(nativeValue->GetInterface(NativeNumber::INTERFACE_ID));
    return napi_clear_last_error(env);
}

参数说明:

  • [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
  • [in] value: 传入要转换的napi_value类型数据对象(可视为一个JS对象)。
  • [out] result: 转换出对应类型(double)结果。 返回值:返回napi_ok表示转换成功,其他值失败。

获取参数的C/C++类型的值前,需要先判断值的类型,本示例需要判断传入参数的JS值必须为number类型

  • NAPI框架提供了napi_typeof方法用于获取指定对象的类型,其函数定义如下:
// Methods to get the native napi_value from Primitive type
NAPI_EXTERN napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result)
{
    CHECK_ENV(env);
    CHECK_ARG(env, value);
    CHECK_ARG(env, result);

    auto nativeValue = reinterpret_cast<NativeValue*>(value);

    *result = (napi_valuetype)nativeValue->TypeOf();
    return napi_clear_last_error(env);
}

参数说明:

  • [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
  • [in] value: 传入要转换的napi_value类型数据对象(可视为一个JS对象)。
  • [out] result: 返回value参数对应的JS类型。
    • napi_valuetype对应了ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型。
    • 另外,napi_valuetype还包括了一个napi_external类型,其表示没有任何属性也没有任何原型的对象。

综上所述参数类型判断及值转换,示例代码如下:

static napi_value Add(napi_env env, napi_callback_info info) {
    // 1\. 获取2个参数,值的类型是js类型(napi_value)
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    // 2\. 获取并判断js参数类型
    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    //输入的数据类型异常处理
    if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, NULL, "Wrong arguments. 2 numbers are expected.");
    return NULL;
  }

    // 3\. 将js类型(napi_value)的参数值转换成C++类型double
    double value0;
    NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));

    double value1;
    NAPI_CALL(env, napi_get_value_double(env, args[1], &value1));

    napi_value sum;
    return sum;
计算结果转换为JS类型并返回
static napi_value Add(napi_env env, napi_callback_info info) {
···
  // 4\. 将结果由C++类型(double)转换成js类型(napi_value)
  napi_value sum;
  NAPI_CALL(env, napi_create_double(env, value0 + value1, &sum));
··· 
}
  • 计算的结果是C/C++类型,需要转换成NAPI node_value类型返回给JS。

  • NAPI提供了一些方法以便将C/C++不同类型的值转为node_value类型,返回给JS代码。例如:

    • napi_create_double
    • napi_create_int32
    • napi_create_uint32
    • napi_create_int64
    • napi_create_string_latin1
    • napi_create_string_utf8
    • napi_create_string_utf16
  • 以napi_create_double方法为例,函数定义如下:

NAPI_EXTERN napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result)
{
    CHECK_ENV(env);
    CHECK_ARG(env, result);

    auto engine = reinterpret_cast<NativeEngine*>(env);
    auto resultValue = engine->CreateNumber(value);

    *result = reinterpret_cast<napi_value>(resultValue);
    return napi_clear_last_error(env);
}

参数说明:
[in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可.
[in] value: 传入要转换的double类型数据值.
[out] result: 转换出结果

ArkUI应用实现代码

ArkUI应用实现目录结构

index.ets内容如下:

index.ets

import hellonapi from '@ohos.hellonapi'

@Entry
@Component
export struct HelloNAPI {
  private textInputController1: TextInputController = new TextInputController()
  private textInputController2: TextInputController = new TextInputController()
  private tittle: string = 'C/C++与JS的数据类型转换'
  private message: string = '计算x+y'
  private tipsNum1: string = '请输入X:'
  private tipsNum2: string = '请输入Y:'
  private tipsResult: string = '结果:'
  private buttonSubmit: string = '计算'
  @State result: number = 0.0
  @State num1: number = 0.0
  @State num2: number = 0.0

  build() {
    Row() {
      Column() {
        Row(){
          Text(this.tittle).height('100%').align(Alignment.Center).fontSize(50).fontWeight(800)
        }.height('30%').width('100%').justifyContent(FlexAlign.Center)

        Row(){
          Text(this.message).height('100%').align(Alignment.Center).fontSize(35).fontWeight(500)
        }.height('15%').width('100%').justifyContent(FlexAlign.Center)

        Row(){
          Text(this.tipsNum1).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'X', controller:this.textInputController1}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num1 = parseFloat(value)})
        }.height('6%').width('100%').justifyContent(FlexAlign.Start)

        Row(){
          Text(this.tipsNum2).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'Y', controller:this.textInputController2}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num2 = parseFloat(value)})
        }.height('6%').width('100%').margin({top:20})

        Row(){
          Text(this.tipsResult).fontColor(Color.Black).fontSize(35).width('40%').height('100%').margin({left:30})
          Text(''+this.result).fontColor(Color.Black).fontSize(35).width('60%').height('100%')
        }.height('10%').width('100%').touchable(false)

        Row(){
          Button(this.buttonSubmit)
            .fontSize(37)
            .fontWeight(FontWeight.Bold)
            .margin({top:5})
            .height(80)
            .width(200)
            .onClick(() => {

              //hellonapi为BUILD.gn文件中定义的ohos_shared_library结构体名称
              this.result = hellonapi.add(this.num1,this.num2)
            })
        }.height('30%').width('100%').justifyContent(FlexAlign.Center)
      }
      .width('100%')
    }
    .height('100%')
  }
}

效果图如下:

index.ets解析

  • 参数说明
字段类型说明
tittlestring标题
messagestring说明
tipsNum1number提示输入第一个参数
tipsNum2number提示输入第二个参数
tipsResultstring提示结果
buttonSubmitstring计算按钮名称
resultstring结果
num1number输入的第一个数
num2number输入的第二个数
  • 设置参数
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
export struct HelloNAPI {
  private textInputController1: TextInputController = new TextInputController()
  private textInputController2: TextInputController = new TextInputController()
  private tittle: string = 'C/C++与JS的数据类型转换'
  private message: string = '计算x+y'
  private tipsNum1: string = '请输入X:'
  private tipsNum2: string = '请输入Y:'
  private tipsResult: string = '结果:'
  private buttonSubmit: string = '计算'
  @State result: number = 0.0
  @State num1: number = 0.0
  @State num2: number = 0.0
  ...
  build() {
   ...
  }
}
  • 界面实现
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
export struct HelloNAPI {
  ...
 build() {
    Row() {
      Column() {
        Row(){
          Text(this.tittle).height('100%').align(Alignment.Center).fontSize(50).fontWeight(800)
        }.height('30%').width('100%').justifyContent(FlexAlign.Center)

        Row(){
          Text(this.message).height('100%').align(Alignment.Center).fontSize(35).fontWeight(500)
        }.height('15%').width('100%').justifyContent(FlexAlign.Center)

        Row(){
          Text(this.tipsNum1).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'X', controller:this.textInputController1}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num1 = parseFloat(value)})
        }.height('6%').width('100%').justifyContent(FlexAlign.Start)

        Row(){
          Text(this.tipsNum2).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'Y', controller:this.textInputController2}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num2 = parseFloat(value)})
        }.height('6%').width('100%').margin({top:20})

        Row(){
          Text(this.tipsResult).fontColor(Color.Black).fontSize(35).width('40%').height('100%').margin({left:30})
          Text(''+this.result).fontColor(Color.Black).fontSize(35).width('60%').height('100%')
        }.height('10%').width('100%').touchable(false)

        Row(){
          Button(this.buttonSubmit)
            .fontSize(37)
            .fontWeight(FontWeight.Bold)
            .margin({top:5})
            .height(80)
            .width(200)
            .onClick(() => {

              //hellonapi为BUILD.gn文件中定义的ohos_shared_library结构体名称
              this.result = hellonapi.add(this.num1,this.num2)
            })
        }.height('30%').width('100%').justifyContent(FlexAlign.Center)
      }
      .width('100%')
    }
    .height('100%')
  }
}
  • 绑定事件、关联参数
    两个TextInput组件分别绑定onChange事件,并分别关联num1,num2来记录输入的参数
        Row(){
          Text(this.tipsNum1).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'X', controller:this.textInputController1}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num1 = parseFloat(value)})
        }.height('6%').width('100%').justifyContent(FlexAlign.Start)

        Row(){
          Text(this.tipsNum2).fontColor(Color.Black).fontSize('65px').width('30%').height('100%').margin({left:30})
          TextInput({ placeholder: 'Y', controller:this.textInputController2}).type(InputType.Number)
            .height('100%').width('60%').margin({left:10,right:30}).fontSize('25px')
            .onChange(value =>{this.num2 = parseFloat(value)})
        }.height('6%').width('100%').margin({top:20})

  • Button组件添加点击事件,调用hellonapiu.cpp中的Add方法(调用js中的add,add和Add已经在napi.cpp中绑定)
Row(){
          Button(this.buttonSubmit)
            .fontSize(37)
            .fontWeight(FontWeight.Bold)
            .margin({top:5})
            .height(80)
            .width(200)
            .onClick(() => {

              //hellonapi为BUILD.gn文件中定义的ohos_shared_library结构体名称
            this.result = hellonapi.add(this.num1,this.num2)
            })
  • 通过NAPI框架输入到C的Add函数的JS参数是num1和num2,输出的JS参数是result

@ohos.hellonapi.d.ts接口文档

declare namespace hellonapi {
	export const add: (a: number, b: number) => number;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

总结

hellonapi.cpp

index.ets

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/537914.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于LNMP部署wordpress

目录 一.环境准备 二.配置源并安装 三.配置Nginx 四.配置数据库 五.上传源码并替换 六.打开浏览器&#xff0c;输入虚拟机ip访问安装部署 七.扩展增加主题 一.环境准备 centos7虚拟机 关闭防火墙和seliunx stop firewalld #关闭防火墙 setenforce 0 …

隐身打击云函数CDN对抗 | 应急响应

0x00 简介 在攻防演练中&#xff0c;使用云函数来隐藏 C&C 的 ip 地址已经成为了一种“标配” 在应急处置过程中&#xff0c;我们经常遇到 netstat -pantu | grep ip 无法找到安全设备关于红队外联的告警的情况 由于 C&C 的 ip 地址是一直变化的&#xff0c;所以常…

基于深度学习的智能停车场车牌识别计费系统(完整程序+训练数据集+开题报告+论文))

摘要 本篇论文研究的是基于车牌识别技术的智能停车场管理系统&#xff0c;采用基于深度学习的车牌识别算法&#xff0c;通过卷积神经网络对车牌图像进行处理和分析&#xff0c;实现车牌字符的识别和车牌信息的提取。同时&#xff0c;本文还设计了一个智能停车场管理系统…

【网安播报】GitHub上的恶意Visual Studio 项目推送 Keyzetsu 恶意软件

1、GitHub 上的恶意 Visual Studio 项目推送 Keyzetsu 恶意软件 威胁行为者正在滥用 GitHub 自动化功能和恶意 Visual Studio 项目来推送“Keyzetsu”恶意软件的新变种并窃取加密货币付款。攻击者创建了GitHub 存储库&#xff0c;并使用各种方法来人为地提高其在平台上的受欢迎…

计费管理系统

武汉理工大学程序设计综合实验作业&#xff0c;没有完全按照要求的文件来写&#xff0c;仅供参考。 目录 菜单说明 大致思路说明 代码实现 func.h 用于存放各种功能函数的声明 tool.h 用于存放相关工具函数的声明 func.c 用于存放各种功能函数的定义 tool.c 用于存放相…

网站如果在日益变化的网络攻击中寻到一线生机

一、引言 在数字化浪潮席卷全球的今天&#xff0c;网络空间早已成为国家安全、经济发展和社会稳定的战略高地。然而&#xff0c;这片看似平静的虚拟世界&#xff0c;实则暗流涌动&#xff0c;网络攻击层出不穷&#xff0c;手段日益翻新&#xff0c;给网站的安全运营带来了前所…

蓝桥杯2022年第十三届省赛真题-最优清零方案 java

样例输入、输出&#xff1a; 输入1&#xff1a; 4 2 1 2 3 4输出1 6输入2&#xff1a; 4 2 1 2 3 4输出2 6解法&#xff1a; 滑动窗口解法如下。主要思路就是&#xff1a;用长度为k的滑动窗口&#xff0c;每遇到连续k个不为0的数&#xff0c;记录这k个数中的最小值为min&…

Nerf-Studio复现笔记

文章目录 1. Env2. Train3. Custom data3.1 Prepare3.2 Render and eval3.3 Results 4. Summary 1. Env The configuration process was smooth on Linux, but there are some problems with tiny_cuda_nn and colmap in Windows. // According to the installation document…

【MATLAB源码-第186期】matlab基于MLE算法的8天线阵列DOA估计仿真,对比粗估计、精确估计输出RMSE对比图。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 第一部分&#xff1a;基本概念与系统设置 方向到达估计&#xff08;Direction of Arrival, DOA&#xff09;是信号处理中一项重要的技术&#xff0c;主要用于确定信号的到达方向。这种技术在雷达、无线通信和声纳等领域中有…

Solana主网使用自定义的RPC进行转账

1、引言 如果用 browser 连接主网的 RPC server 会收到 error code 403 message 為 Access forbidden, contact your app developer or supportrpcpool.com. 错误&#xff0c;因为主网的 RPC server 会检查 HTTP Header 如果判断出來是 browser 就会报告 403 錯誤。 要解決这…

LabVIEW闭环步进电机运动系统设计及精度分析

LabVIEW闭环步进电机运动系统设计及精度分析 在自动化设备不断发展的当代&#xff0c;闭环步进电机以其高精度和可靠性成为了自动化设备的重要组成部分。以LabVIEW软件为核心&#xff0c;结合运动控制卡及驱动器模块&#xff0c;设计并实现了一个闭环步进电机的多轴运动控制系…

加盟馅饼多少钱合适,加盟哪个馅饼品牌最好?

加盟馅饼&#xff0c;成本是创业者首要考虑的问题。合适的加盟费用应该考虑到品牌知名度、培训支持、店面选址等因素。一般而言&#xff0c;加盟馅饼的费用在几万元至数十万元之间&#xff0c;具体费用因品牌而异。重要的是&#xff0c;加盟费用不应是唯一的考量因素&#xff0…

SpringBoot3 + Vue3 + Uniapp + uView + Elenment 实现动态二级分类以及二级分类的管理

SpringBoot3 Vue3 Uniapp uView Elenment 实现动态二级分类以及二级分类的管理 1. 效果展示1.1 前端显示效果1.2 后台管理一级分类1.3 后台管理二级分类 2. 后端代码2.1 GoodsCategoryController.java2.2.1 GoodsCategoryMapper.java2.2.2 GoodsCategorySonMapper.java2.3.…

Pytest精通指南(06)Fixture scope作用域详解

文章目录 前言Scope 作用域写在测试用例函数文件写在conftest.py文件作用域总结验证默认作用域验证执行顺序遵循验证类中的fixture作用域验证重名fixture作用域 前言 从前文中&#xff0c;我们已经知道固件&#xff08;fixture&#xff09;的概念、原理、作用域&#xff0c;并且…

【年度典型案例】扫码就能领补贴?通知社保在线速办?当心是钓鱼骗局!

随着我们生活的数字化程度越来越高&#xff0c;完成各种业务和服务变得前所未有的便捷。只需轻轻一点手机屏幕&#xff0c;我们办事儿变得飞快又方便。然而&#xff0c;正当我们享受这种数字化带来的便捷时&#xff0c;一些不法分子也在暗中伺机而动&#xff0c;利用各种手段制…

k8s知识

k8s是用于容器编排和管理的&#xff0c;docker或者ctr是k8s的运行时&#xff0c;k8s通过容器运行时来启动容器&#xff0c;容器启动需要镜像&#xff0c;镜像可以用docker构建&#xff0c;dockerfile就是用于自定义如何构建镜像&#xff0c;所以上面那套流水线就是先用dockerfi…

Java算法小练习——五道经典算法题

练习一&#xff1a;按照要求进行排序 定义数组并存储一些朋友对象&#xff0c;利用Arrays中sort方法进行排序 要求1&#xff1a;属性有姓名、年龄、身高。 要求2&#xff1a;按照年龄的大小进行排序&#xff0c;年龄一样&#xff0c;按身高排序&#xff0c;身高一样安姓名的字母…

策略为王股票软件源代码-----如何修改为自己软件05

上面是如何修改里面的图标和图片,,, 试用版下载: http://www.ninebulls.com/ 联系方式: support@ninebulls.com 常见问题: 1。源代码经编程后产生的目标文件执行后显示为试用版,这样是否正常?如何切换成专业版? 显示为评估版是正常的,注册后即切换成专业版。 Too…

【算法一则】做算法学数据结构 - 简化路径 - 【栈】

目录 题目栈代码题解 题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 ‘/’ 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表…

python使用ffmpeg分割视频为Hls分片文件/使用OpenSSL加密m3u8和TS文件

FFmpeg和OpenSSL是一个开源免费的软件&#xff0c;在官网上就能下载&#xff0c; FFmpage网址&#xff08;建议选择文件名full结尾的文件&#xff09;&#xff1a;Builds - CODEX FFMPEG gyan.dev OpenSSL网址&#xff08;建议选择win64的MSI文件&#xff09;&#xff1a;Win3…