# 16.4:NAPI方法扩展

在前 3 小结笔者简单介绍了 NAPI 工程并对生成的源码进行了简单介绍,本节笔者在前 3 小节的基础上对 NAPI 工程做个扩展,再额外添加一个计算 MD5 的方法 md5()

# 16.4.1:声明md5方法

index.d.ts 文件中声明一个 md5() 方法,该方法接收一个 string 参数,返回类型也是 string 类型,表示经过 MD5 计算后的值,样例代码如下所示:

export const add: (a: number, b: number) => number;

// 声明 md5 方法
export const md5: (value: string) => string;
1
2
3
4

# 16.4.2:映射C++方法

在前 3 节的介绍中我们知道,JS 端声明的方法由 C++ 端实现时需要把两端的方法做个映射,因此先在 hello.cppInit() 方法内设置 md5() 方法为 C++ 的 Md5() 方法,样例代码如下所示:

static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
      { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
      // 设置JS的md5()方法的C++实现为Md5()方法,其它参数默认即可
      { "md5", nullptr, Md5, nullptr, nullptr, nullptr, napi_default, nullptr }
    };

    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
1
2
3
4
5
6
7
8
9
10
11

# 16.4.3:实现C++代码

Init() 方法中配置了 C++ 的 Md5() 方法后,需要实现 Md5() 方法,样例代码如下所示:

static napi_value Md5(napi_env env, napi_callback_info info) {

    // 1、从info中取出JS传递过来的参数放入args
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1000", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、获取参数的类型
    napi_valuetype stringType;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1001", "napi_typeof error");
        return nullptr;
    }

    // 3、如果参数为null,则抛异常
    if (napi_null == stringType) {
        napi_throw_error(env, "-1002", "the param can't be null");
        return nullptr;
    }

    // 4、获取传递的string长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1003", "napi_get_value_string_utf8 error");
        return nullptr;
    }

    // 5、如果传递的是"",则抛异常
    if (length == 0) {
        napi_throw_error(env, "-1004", "the param length invalid");
        return nullptr;
    }

    // 6、读取传递的string参数放入buffer中
    char* buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1005", "napi_get_value_string_utf8 error");
        return nullptr;
    }

    // 7、模拟MD5加密操作,直接给传递进来的string后追加[NAPI]
    std::string str = buffer;
    str = str + "[NAPI]";

    // 8、把C++数据转成napi_value
    napi_value value = nullptr;
    const char* md5 = str.c_str();
    if (napi_ok != napi_create_string_utf8(env, md5, strlen(md5), &value)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_create_string_utf8 error");
        return nullptr;
    }

    // 9、资源清理
    delete[] buffer;
    buffer = nullptr;

    // 返回 value
    return value;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

Md5() 方法的解释说明的很清楚:第 1 步从 napi_callback_info 中读取传递进来的参数放入 args 中;第 2 步读取传递的参数类型;第 3 步判断参数类型是否为 null,如果为 null 则抛异常;第 4 步读取参数长度;第 5 步如果传递的参数为 "" 则抛异常;第 6 步根据参数长度把参数读取出来放入 buffer 里,第 7 布模拟 MD5 操作直接在参数末尾追加 [NAPI] 字符串,第 8 步把 C++ 类型转换成 napi_value 类型,第 9 布清理资源,第 10 步返回结果。

📢:读取 string 时先读取参数的长度,再读取内容。

# 16.4.4:测试C++方法

根据 Md5() 方法的实现,限制条件是不允许数据 null"",如果输入则抛异常。因此可测试以下三种场景:正常参数,null 参数和 "" 参数,样例代码如下所示:

import testNapi from 'libentry.so'

@Entry @Component struct Index {

  @State message: string = 'Hello,OpenHarmony'

  build() {
    Column({space: 10}) {

      Text(this.message)
        .fontSize(20)

      Button("正常参数")
        .onClick(() => {
          this.message = testNapi.md5("Hello, OpenHarmony")
        })

      Button("null参数")
        .onClick(() => {
          this.message = testNapi.md5(null);
        })

      Button("\"\"参数")
        .onClick(() => {
          this.message = testNapi.md5("");
        })
    }
    .padding(10)
    .width('100%')
    .height("100%")
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

点解 正常参数 按钮,样例运行结果如下所示:

16_4_4_1

点击 null参数 测试异常情况,异常日志如下:

02-28 23:50:56.176 2093-2093/com.example.oh_0400_napi E C03f00/ArkCompiler: [default] Call:1307 occur exception need return
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: [jsi_base_utils.cpp(ReportJsErrorEvent)-(0)] summaryBody:
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: Lifetime: 0.000000s
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: Js-Engine: ark
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: page: pages/Index.js
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: Error message: the param can't be null
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: SourceCode:
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: this.message = testNapi.md5(null);
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: ^
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: Stacktrace:
02-28 23:50:56.177 2093-2093/com.example.oh_0400_napi E C03900/Ace: at anonymous (/ets/pages/Index.ets:20:26)
1
2
3
4
5
6
7
8
9
10
11

# 16.4.5:小结

本节笔者在前 3 小节的基础上简单实现了 C++ 端的 md5() 方法并实现 JS 端调用,该 md5() 方法是一个模拟,目的是跑通 JS 到 C++的调用流程,下节笔者将要介绍如何引入三方库实现真正的 MD5 计算。

请作者喝杯咖啡

津公网安备 12011402001367号

津ICP备2020008934号-2

中央网信办互联网违法和不良信息举报中心

天津市互联网违法和不良信息举报中心