跟普通项目相比,主要区别是多了一个cpp文件夹、oh-package.json5中的dependencies引入还有build-profile.json5中的externalNativeOptions配置,abiFilters是支持的CPU架构,目前移动端项目只支持arm64-v8a、x86_64两种。
普通项目也可以复制以下文件/配置到对应位置变成一个Native C++项目。但要注意把一些entry的字符替换成你的module名称。如果同样是entry就不用修改。
新建两个文件test_c.cpp和test_c.h,位置如下
- #ifndef NATIVETEST_TEST_C_H
- #define NATIVETEST_TEST_C_H
-
- class test_c {
- public:
- explicit test_c();
- ~test_c();
-
- int add(int a, int b);
- int sub(int a, int b);
- };
-
- #endif //NATIVETEST_TEST_C_H
- #include "test_c.h"
- test_c::test_c() {}
- test_c::~test_c() {}
- int test_c::add(int a, int b) {
- return a + b;
- }
-
- int test_c::sub(int a, int b) {
- return a - b;
- }
前面都是建项目时默认写好的语句,主要加了一行add_library(test_c SHARED test_c.cpp)
- cmake_minimum_required(VERSION 3.5.0)
- #…………中间省略
- #第一个test_c是指将要生成的so文件名,最终的名称会变成libtest_c.so
- add_library(test_c SHARED test_c.cpp)
点击Build -> Build Hap(s)/APP(s) -> Build Hap(s), 生成的so文件在以下目录。生成后就可以把test_c.cpp删掉了。
在cpp目录下建一个libs文件夹,按照CPU架构把so文件放到对应的文件夹中(同时把所有.h头文件放到include文件夹中,这里因为在上个步骤中已经写好了test_c.h,所以只需放入so文件)
主要是加了最后一行target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/libs/${OHOS_ARCH}/xxxxx.so),具体so文件的名称自行修改
- # the minimum version of CMake.
- cmake_minimum_required(VERSION 3.5.0)
- #项目名称,创建项目时填的值
- project(NativeTest)
-
- set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
-
- if(DEFINED PACKAGE_FIND_FILE)
- include(${PACKAGE_FIND_FILE})
- endif()
-
- # 添加头文件.h目录,包括cpp,cpp/include,告诉cmake去这里找到代码引入的头文件
- # 一般头文件都放在cpp/include下
- include_directories(${NATIVERENDER_ROOT_PATH}
- ${NATIVERENDER_ROOT_PATH}/include)
-
- add_library(entry SHARED napi_init.cpp)
- target_link_libraries(entry PUBLIC libace_napi.z.so)
-
- #add_library(test_c SHARED test_c.cpp)
- #会根据不同的架构去不同的目录下找到对应的so文件
- target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/libs/${OHOS_ARCH}/libtest_c.so)
以下代码可以复制进创建项目时生成的napi_init.cpp文件中
- #include "test_c.h"
- // 如果是用C编写的库,需要在extern "C"{}中包裹,否则会出现链接错误
- // extern "C"{
- // #include "test_c.h"
- // }
- #include
//日志输出所需要的库 - #pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
- void test_demo()
- {
- // 这里只是演示调用引用的so库的方法
- test_c test;
- int r1 = test.add(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
- int r2 = test.sub(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
- }
napi_init.cpp完整代码
- #include "napi/native_api.h"
- #include "test_c.h"
- // 如果是用C编写的库,需要在extern "C"{}中包裹,否则会出现链接错误
- // extern "C"{
- // #include "test_c.h"
- // }
- #include
//日志输出所需要的库 - #pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
- void test_demo()
- {
- // 这里只是演示调用引用的so库的方法
- test_c test;
- int r1 = test.add(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
- int r2 = test.sub(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
- }
- static napi_value Add(napi_env env, napi_callback_info info)
- {
- test_demo();
- size_t argc = 2;
- napi_value args[2] = {nullptr};
- napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
- napi_valuetype valuetype0;
- napi_typeof(env, args[0], &valuetype0);
-
- napi_valuetype valuetype1;
- napi_typeof(env, args[1], &valuetype1);
-
- double value0;
- napi_get_value_double(env, args[0], &value0);
-
- double value1;
- napi_get_value_double(env, args[1], &value1);
-
- napi_value sum;
- napi_create_double(env, value0 + value1, &sum);
-
- return sum;
-
- }
-
- EXTERN_C_START
- static napi_value Init(napi_env env, napi_value exports)
- {
- 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;
- }
- EXTERN_C_END
-
- 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 },
- };
-
- extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
- {
- napi_module_register(&demoModule);
- }
在index.d.ts文件中,提供ArkTS/JS侧的接口方法。
- //一个同步返回的加法计算
- export const add: (a: number, b: number) => number;
- //一个异步返回的加法计算
- export const addWithCallBack: (a: number, b: number, callBack: (result:number) => void) => number;
- //各种数据类型的参数传参demo,这里只列了几个常见的
- export const paramsTest: (a: number, b: string, c:boolean, d:string[], e:ArrayBuffer) => void;
在index.d.ts定义的接口需在napi_init.cpp有对应实现。
- #include "napi/native_api.h"
- #include "test_c.h"
- #include
- #include
//日志输出所需要的库 - #pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
- void test_demo()
- {
- // 这里只是演示调用引用的so库的方法
- test_c test;
- int r1 = test.add(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
- int r2 = test.sub(10, 5);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
- }
-
- //一个同步返回的加法计算
- static napi_value Add(napi_env env, napi_callback_info info)
- {
- test_demo();//测试调用so库的方法
-
- size_t argc = 2; // 参数个数
- napi_value args[2] = {nullptr};
- napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
-
- // 获取第一个参数
- double value0;
- napi_get_value_double(env, args[0], &value0);
- // 获取第二个参数
- double value1;
- napi_get_value_double(env, args[1], &value1);
-
- // 返回值
- napi_value sum;
- napi_create_double(env, value0 + value1, &sum);
- return sum;
- }
-
- //定义需要传递给异步工作的数据结构
- struct CallbackContext {
- napi_env env = nullptr;
- napi_ref recvCallbackRef = nullptr;
- napi_async_work work;
- //需要传入的参数
- double a;
- double b;
- //返回的参数
- double result;
- };
-
- // 这里可以进行耗时操作, 方法名可修改,但参数固定
- void AddAsync(napi_env env, void *data) {
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddAsync is called");
- // 获取传入的参数
- CallbackContext *context = (CallbackContext *)data;
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "context.a: %{public}f", context->a);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "context.b: %{public}f", context->b);
- // 模拟耗时操作
- std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 睡眠1秒
- // 计算结果
- context->result = context->a + context->b;
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddAsync end");
- }
-
- //AddAsync执行完毕后会自动调用这个方法, 方法名可修改,但参数固定
- void AddCallBack(napi_env env, napi_status status, void *data) {
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddCallBack is called");
- CallbackContext *context = (CallbackContext *)data;
- napi_value recvCallback = nullptr;
- napi_get_reference_value(context->env, context->recvCallbackRef, &recvCallback);
- // 因为回调方法只有一个参数, 若有多个参数要给每个参数都赋值
- int size = 1;
- napi_value argv[size];
- napi_create_double(env, context->result, &argv[0]);
-
- napi_value ret;
- napi_call_function(env, nullptr, recvCallback, size, argv, &ret);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddCallBack delete");
- napi_delete_reference(context->env, context->recvCallbackRef);
- napi_delete_async_work(context->env, context->work);
- delete context;
- }
-
- //一个异步返回的加法计算
- static napi_value AddWithCallBack(napi_env env, napi_callback_info info)
- {
- size_t argc = 3;
- napi_value args[3] = {nullptr};
- napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
-
- double value0;
- napi_get_value_double(env, args[0], &value0);
-
- double value1;
- napi_get_value_double(env, args[1], &value1);
-
- // 获取回调函数
- napi_ref recvCallbackRef;
- napi_create_reference(env, args[2], 1, &recvCallbackRef);
-
- CallbackContext *context = new CallbackContext;
- context->env = env;
- context->recvCallbackRef = recvCallbackRef;
- context->a = value0;
- context->b = value1;
- //异步调用
- napi_value resource;
- //第二个参数为当前方法名
- napi_create_string_latin1(context->env, "AddWithCallBack", NAPI_AUTO_LENGTH, &resource);
- //创建异步工作,AddAsync为耗时操作的方法,AddCallBack为耗时操作完成后的回调方法,可替换成自己实际所需的方法
- napi_create_async_work(context->env, nullptr, resource, AddAsync, AddCallBack, context,
- &context->work);
- napi_queue_async_work(context->env, context->work); // 实现在UI主线程调用
-
- // 直接返回空值,实际返回值通过回调方法返回
- napi_value result = nullptr;
- napi_get_undefined(env, &result);
- return result;
- }
-
- static napi_value ParamsTest(napi_env env, napi_callback_info info)
- {
- size_t argc = 5;
- napi_value args[5] = {nullptr};
- napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
-
- //获取第一个参数--数字类型
- double a;
- napi_get_value_double(env, args[0], &a);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "double a: %{public}f", a);
-
- //获取第二个参数--字符串类型
- size_t typeLen = 0;
- napi_get_value_string_utf8(env, args[1], nullptr, 0, &typeLen);
- char *b = new char[typeLen + 1];
- napi_get_value_string_utf8(env, args[0], b, typeLen + 1, &typeLen);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "string b: %{public}s", b);
-
- //获取第三个参数--bool类型
- bool c;
- napi_get_value_bool(env, args[2], &c);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "boolean c: %{public}d", c);
-
- //第四个参数--数组类型
- // 检查参数是否为数组
- bool is_array;
- napi_is_array(env, args[3], &is_array);
- if (!is_array) {
- napi_throw_type_error(env, nullptr, "Argument must be an array");
- return nullptr;
- }
- // 获取数组长度
- uint32_t length;
- napi_get_array_length(env, args[3], &length);
- // 遍历数组
- for (int i = 0; i < length; i++) {
- napi_value result;
- napi_get_element(env, args[3], i, &result);
-
- size_t len = 0;
- napi_get_value_string_utf8(env, result, nullptr, 0, &len);
- char *text = new char[len + 1];
- napi_get_value_string_utf8(env, result, text, len + 1, &len);
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "array d[%{public}d]: %{public}s", i, text);
- }
-
- //获取第五个参数--ArrayBuffer类型
- bool is_array_buffer;
- // 检查第五个参数是否为ArrayBuffer
- napi_is_arraybuffer(env, args[4], &is_array_buffer);
- if (is_array_buffer) {
- napi_value array_buffer_value;
- uint8_t *data;
- size_t byte_length;
-
- array_buffer_value = args[4];
- // 获取ArrayBuffer的外部数据指针和长度
- napi_get_arraybuffer_info(env, array_buffer_value, (void **)&data, &byte_length);
-
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "sizeof(uint8_t) = %{public}d", (int)sizeof(uint8_t));
-
- // 使用data指针处理ArrayBuffer数据
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "arraybuffer size = %{public}d, (ie: bytes=%{public}d) ",
- (int)(byte_length / sizeof(uint8_t)), (int)byte_length);
-
- // for (int i = 0; i < ((int)(byte_length / sizeof(uint8_t))); ++i) {
- // OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "data[%{public}d] = %{public}d ", i, *(data + i));
- // }
- } else {
- // 参数不是ArrayBuffer的处理逻辑
- OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "e is not ArrayBuffer");
- }
-
- napi_value result = nullptr;
- napi_get_undefined(env, &result);
- return result;
- }
-
- EXTERN_C_START
- static napi_value Init(napi_env env, napi_value exports)
- {
- napi_property_descriptor desc[] = {
- //第一个参数是index.d.ts定义的方法名,第三个参数是当前cpp文件中的方法名
- { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
- { "addWithCallBack", nullptr, AddWithCallBack, nullptr, nullptr, nullptr, napi_default, nullptr },
- { "paramsTest", nullptr, ParamsTest, nullptr, nullptr, nullptr, napi_default, nullptr }
- };
- napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
- return exports;
- }
- EXTERN_C_END
-
- 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 },
- };
-
- extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
- {
- napi_module_register(&demoModule);
- }
直接修改默认创建的Index.ets
- import { hilog } from '@kit.PerformanceAnalysisKit';
- import testNapi from 'libentry.so';//导入C++模块
-
- @Entry
- @Component
- struct Index {
- callBack = (result:number)=>{
- hilog.info(0x0000, 'testTag', 'addWithCallBack(4,5) = %{public}d', result);
- }
-
- build() {
- Row() {
- Column({space:10}) {
- Button('add(2,3)')
- .onClick(()=>{
- hilog.info(0x0000, 'testTag', 'add(2,3) = %{public}d', testNapi.add(2, 3));
- })
-
- Button('addWithCallBack(4,5)')
- .onClick(()=>{
- hilog.info(0x0000, 'testTag', 'addWithCallBack(4,5) Begin');
- testNapi.addWithCallBack(4,5,this.callBack)
- })
-
- Button('paramsTest')
- .onClick(()=>{
- //获取一个arraybuffer数据,本地有什么图片资源就用哪个就行
- getContext().resourceManager.getMediaContent($r('app.media.app_icon')).then((mediaContent)=>{
- hilog.info(0x0000, 'testTag', 'paramsTest getMediaContent success');
- hilog.info(0x0000, 'testTag', 'paramsTest Begin');
- testNapi.paramsTest(4, "text", false, ['apple','boy','cat'],mediaContent.buffer)
- })
- })
- }
- .width('100%')
- }
- .height('100%')
- }
- }