1. Background Introduction
HarmonyOS mainly provides ArkTS and C++ as development languages:
- ArkTS is the preferred main application development language for HarmonyOS. ArkTS has been further extended on the basis of the TypeScript (TS for short) ecosystem around application development. It maintains the basic style of TS and, at the same time, strengthens static inspection and analysis during the development period through specification definitions to improve the stability and performance of program execution. ArkTS is suitable for scenarios of efficient UI interface development.
- C++ is supported in the form of the NDK (Native Development Kit) toolset. NDK is a collection of Native APIs provided by the HarmonyOS SDK, corresponding compilation scripts, and compilation toolchains, which facilitates developers to use the C or C++ languages to implement the key functions of applications. NDK only covers some basic underlying capabilities of HarmonyOS, such as the C runtime basic library libc, graphics library, window system, multimedia, compression library, Node-API for cross-language between ArkTS/JS and C, etc., and does not provide the complete capabilities of ArkTS/JS APIs. The C++ language is suitable for the following scenarios:
- Performance-sensitive scenarios, such as games, physical simulations, and other computation-intensive scenarios.
- Scenarios where existing C or C++ libraries need to be reused.
- Scenarios where specialized customized libraries need to be developed according to CPU characteristics, such as Neon acceleration.
Some capabilities of HarmonyOS are only supported by C++ interfaces, such as audio and video encoding and decoding. Moreover, we noticed that in the newly released HarmonyOS 5.0.1 version, the newly added capabilities are mainly C APIs. Therefore, to explore more capabilities of HarmonyOS and become a senior HarmonyOS engineer, mixed development of ArkTS and C++ is essential.
The process for ArkTS to call C++ involves a series of complicated procedures. This article mainly introduces the cross-language "glue" code generation tool provided by DevEco Studio to help everyone improve development efficiency.
2. The Process of ArkTS Calling C++ Methods
Similar to Android JNI, in HarmonyOS, the ability for ArkTS to call C++ methods is mainly provided by NAPI. This section takes the Native C++ project Demo newly created by DevEco Studio as an example to introduce the process of ArkTS calling C++ methods. The following figure shows the code structure diagram after creating a Native C++ project:
It mainly consists of three parts:
- types:
- Index.d.ts: It mainly contains the C++ interfaces exposed to ArkTS.
- oh-package.json5: It specifies the dynamic library name and the path of the above Index.d.ts file.
- CMakeLists.txt: The C++ code construction script.
- napi_init.cpp: The C++ implementation code.
The general process of developing a Native module is to implement the corresponding modules mentioned above.
2.1 Declare C++ Interfaces in Index.d.ts
The declared functions are for C++ implementation and can be called in ArkTS.
export const add: (a: number, b: number) => number;
2.2 Register and Implement the Declared Methods in C++
First, register the methods declared in Index.d.ts in the Init method of NAPI:
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
napi_property_descriptor is an array that can implement multiple methods. In the example, the string "add" represents the add method declared in Index.d.ts, and the third parameter Add represents the name of the method implemented in C++. Next, we need to implement the Add method:
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;
}
The structure of the NAPI implementation method is fixed. The return value must be napi_value, and the parameters must be two, namely napi_env and napi_callback_info. In the method, the specific parameter information of napi_callback_info is parsed to implement specific functions.
2.3 Configure the Name of the Compiled Dynamic Library and the List of C++ Source Codes in CMakeLists.txt
add_library is used to specify the name of the library output by compilation and the list of C++ codes participating in the compilation:
add_library(entry SHARED napi_init.cpp)
For knowledge in this aspect, you can refer to the relevant documents of the CMake build tool: https://cmake.org/
2.4 Call the Declared Methods in ArkTS
Finally, after the implementation is completed, the corresponding methods can be called in ArkTS:
// First, import the dynamic library:
import testNapi from 'libentry.so';
// Call the corresponding add method
hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
3. DevEco Studio Quickly Generates Glue Code
From the process of ArkTS calling C++ methods above, we can see that the process is a bit complicated, requiring processing in two files at three places. Is there a convenient method? Don't worry. Next, we will introduce the highlight of today, the cross-language code editing provided by DevEco Studio IDE.
DevEco Studio IDE provides two creation methods:
- Write function declarations in Index.d.ts and generate C++ functions with one click.
- Write C++ function declarations in C++ header files and generate registration codes with one click.
The following is a detailed introduction respectively.
3.1 Write Function Declarations in Index.d.ts and Generate C++ Functions with One Click
After declaring methods in Index.d.ts, the IDE will prompt an error. When you hover the mouse over it, you will get the following prompt:
Click "Generate native implementation", and you can quickly generate the registration code and implementation code in C++:
In this way, three manual implementations are reduced to one, and the function is very practical, improving efficiency by more than two-thirds.
3.2 Write C++ Function Declarations in C++ Header Files and Generate Registration Codes with One Click
For the way of creating C++ function declarations with one click, we need to first create a header file and then declare methods in the header file:
Right-click the method and click Generate:
Continue to click NAPI:
The registration function and the corresponding NAPI function implementation have been generated for us:
The declared methods in Index.d.ts have also been generated for us:
With the help of the IDE, the writing of "glue" code between cross-languages has greatly improved efficiency. It has to be said that the function provided by DevEco Studio is quite good and very considerate for the majority of programmers.
There are three points to note here:
- The header file types supported for quickly generating code:.hpp,.hxx,.hh,.h;
- The currently supported parameter types for functions that quickly generate NAPI by declaration: bool, int, string, void, float, double, std::array, std::vector, etc.;
- If napi_init.cpp has been created in the project, it will be appended in the Init of the file. If it has not been created, the IDE will help us create it automatically.
4. Use AI to Improve the Writing of NAPI Type Conversion Code in C++ Code
With the automatic generation of cross-language glue code provided by the IDE, it has greatly helped us with Native code development. However, some people may still feel that the code in TODO generated is also very complicated, and a lot of cross-language conversion code between ArkTS and C++ is very templated and cumbersome. Is there a corresponding efficiency improvement method?
Taking the automatically generated add code as an example:
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;
}
It is necessary to parse two int-type parameters from napi_callback_info and continue to generate napi_value for return after the operation is completed.
Since this is the NAPI protocol for cross-language data type conversion, the DevEco CodeGenie plugin also provides good support. You can install the Genie plugin in DevEco Studio to help us quickly generate C++ implementation code:
In this way, after pressing the "TAB" key several times, most of it will be generated for us. Then we can implement our own code according to our business logic.
5. Summary
This article mainly introduced the implementation steps of Native C++ code and the cross-language code editing tool provided by DevEco Studio IDE. It can help us generate corresponding initialization registration methods and C++ implementation methods with just writing function declarations and one click. I hope everyone can experience it more and improve our development efficiency through tools.
Top comments (0)