HarmonyOS鸿蒙Next中使用NDK开发基本流程和基础DEMO
HarmonyOS鸿蒙Next中使用NDK开发基本流程和基础DEMO 1、NDK适用场景
- 性能敏感的场景,如游戏、物理模拟等计算密集型场景
- 需要复用已有C或C++库的场景
- 需要针对CPU特性进行专项定制库的场景,如Neon加速
- 纯C或C++的应用不建议使用NDK
- 希望在尽可能多的HarmonyOS设备上保持兼容的应用不建议使用NDK
2、NDK的基本概念
是HarmonyOS中提供ArkTS/JS与C/C++跨语言调用的接口,是NDK接口中的一部分。该接口是在Node.js提供的Node-API基础上扩展而来,但与Node.js中的Node-API不完全兼容。
3、依赖知识模块
- Linux C语言编程知识
- CMake使用知识
- Node Addons开发知识
- Clang/LLVM编译器使用知识
4、NDK工程
1、创建NDK工程
根据工程创建向导,选择Native C++工程模板,然后单机Next。
在工程配置页面,根据向导配置工程的基本信息后,单击Finish,工具会自动生成示例代码和相关资源,等待工程创建完成。
2、构建NDK工程
NDK通过CMake和Ninja编译应用的C/C++代码,编译过程如下图所示。
CMakeLists.txt
通过DevEco Studio模板工程创建的NDK工程中,包含默认生成的CMakeLists.txt脚本,如下所示:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(MyApplication)
# 定义一个变量,并赋值为当前模块cpp目录
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 添加头文件.h目录,包括cpp,cpp/include,告诉cmake去这里找到代码引入的头文件
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# 声明一个产物libentry.so,SHARED表示产物为动态库,hello.cpp为产物的源代码
add_library(entry SHARED hello.cpp)
# 声明产物entry链接时需要的三方库libace_napi.z.so
# 这里直接写三方库的名称是因为它是在ndk中,已在链接寻址路径中,无需额外声明
target_link_libraries(entry PUBLIC libace_napi.z.so)
externalNativeOptions
模块级build-profile.json5中externalNativeOptions参数是NDK工程C/C++文件编译配置的入口,可以通过path指定CMake脚本路径、arguments配置CMake参数、cppFlags配置C++编译器参数、abiFilters配置编译架构等。
"apiType": "stageMode",
"buildOption": {
"arkOptions": {
},
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": "",
"abiFilters": [
"arm64-v8a",
"x86_64"
],
}
}
externalNativeOptions具体参数说明如下表所示。
配置项 | 类型 | 说明 |
---|---|---|
path | string | CMake构建脚本地址,即CMakeLists.txt文件地址。 |
abiFilters | array | 本机的ABI编译环境,包括: - arm64-v8a - x86_64 如不配置该参数,编译时默认编译出arm64-v8a架构相关so。 |
arguments | string | CMake编译参数。 |
cppFlags | string | C++编译器参数。 |
5、DEMO
我们代码开发主要会涉及到三个文件:
- index.d.ts 从C/C++侧导出到ArkTS端的接口声明文件
- napi_init.cpp c++侧具体的代码实现
- index.ets ArkTS侧代码
在C++源码侧新增一个函数
#include "napi/native_api.h"
// 模板自动生成的代码
static napi_value Add(napi_env env, napi_callback_info info)
{
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;
}
// 添加一个Minus函数
static napi_value Minus(napi_env env, napi_callback_info info)
{
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 },
// 将Minus导出,导出的函数名为minus
{ "minus", nullptr, Minus, 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添加增加的函数声明
export const add: (a: number, b: number) => number;
// 新增一个minus函数导出到ArkTS侧
export const minus: (a: number, b: number) => number;
在ArkTS侧使用C++侧导出的接口
import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so'; // 引用C++模块
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
aboutToAppear(): void {
// 调用C++模块的函数接口
hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
hilog.info(0x0000, 'testTag', 'Test NAPI 12 - 3 = %{public}d', testNapi.minus(12, 3));
}
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
C++模块包名的指定:
// src/main/cpp/types/libentry/oh-package.json5
{
"name": "libentry.so",
"types": "./Index.d.ts",
"version": "1.0.0",
"description": ""
}
更多关于HarmonyOS鸿蒙Next中使用NDK开发基本流程和基础DEMO的实战教程也可以访问 https://www.itying.com/category-93-b0.html
依赖知识模块
- Linux C语言编程知识
- CMake使用知识
- Node Addons开发知识
- Clang/LLVM编译器使用知识
更多关于HarmonyOS鸿蒙Next中使用NDK开发基本流程和基础DEMO的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
《使用NDK开发基本流程和基础DEMO》
不错不错。
在HarmonyOS鸿蒙Next中使用NDK(Native Development Kit)开发的基本流程如下:
-
环境准备:确保已安装DevEco Studio和NDK工具链。NDK工具链包括编译器、调试器和库文件,用于编译C/C++代码。
-
创建项目:在DevEco Studio中创建一个新的HarmonyOS项目,选择Native C++模板。这将自动生成一个包含C++代码的项目结构。
-
配置NDK:在项目的
build.gradle
文件中配置NDK路径和编译选项。例如,指定NDK版本、ABI(应用二进制接口)和目标平台。 -
编写C/C++代码:在
cpp
目录下编写C/C++代码。可以使用JNI(Java Native Interface)与Java/Kotlin代码进行交互。 -
编译和构建:使用DevEco Studio的构建工具编译C/C++代码,生成共享库(
.so
文件)。构建过程会自动处理依赖关系和链接库。 -
集成到应用:将生成的共享库集成到HarmonyOS应用中。在Java/Kotlin代码中通过
System.loadLibrary
加载共享库,并调用其中的原生方法。 -
调试和测试:使用DevEco Studio的调试工具对原生代码进行调试。可以设置断点、查看变量值和调用栈。
基础DEMO示例:
-
创建Native C++项目:在DevEco Studio中创建新项目,选择Native C++模板。
-
编写C++代码:在
cpp
目录下创建native-lib.cpp
文件,编写一个简单的C++函数,例如返回字符串。
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
- 配置NDK:在
build.gradle
中配置NDK路径和编译选项。
android {
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
ndkVersion "21.3.6528147"
}
-
编译和构建:点击Build按钮,编译C++代码并生成共享库。
-
集成到应用:在
MainActivity.java
中加载共享库并调用原生方法。
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
- 运行和测试:运行应用,查看TextView中显示的字符串。
以上是HarmonyOS鸿蒙Next中使用NDK开发的基本流程和基础DEMO。
在HarmonyOS鸿蒙Next中使用NDK开发的基本流程如下:
- 环境配置:确保已安装DevEco Studio和NDK工具链。
- 创建项目:在DevEco Studio中创建Native C++项目。
- 编写C/C++代码:在
src/main/cpp
目录下编写Native代码。 - 配置CMake:在
CMakeLists.txt
中配置编译选项和链接库。 - Java/Kotlin调用:通过JNI接口在Java/Kotlin中调用Native方法。
- 编译运行:编译项目并在设备或模拟器上运行。
基础DEMO示例:
// native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
// MainActivity.java
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
通过以上步骤,你可以在HarmonyOS中实现基本的NDK开发。