介紹
本篇Codelab主要介紹如何使用DevEco Studio創建一個Native C++應用。應用采用Native C++模板,實現使用NAPI調用C標準庫的功能。使用C標準庫hypot接口計算兩個給定數平方和的平方根。在輸入框中輸入兩個數字,點擊計算結果按鈕顯示計算后的數值。
相關概念
- [Native API]:NAPI提供的接口名與三方Node.js一致,目前支持部分接口。
- [Native API中支持的標準庫]:目前支持標準C庫、C++庫、OpenSL ES、zlib。
環境搭建
軟件要求
- [DevEco Studio]版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
- 鴻蒙文檔參考:[
gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
硬件要求
- 開發板類型:[潤和RK3568開發板]。
- OpenHarmony系統:3.2 Release。
鴻蒙NEXT學習文檔紫料可mau123789添加v直接領
環境搭建
完成本篇Codelab我們首先要完成開發環境的搭建,本示例以RK3568開發板為例,參照以下步驟進行:
- [獲取OpenHarmony系統版本]:標準系統解決方案(二進制)。以3.2 Release版本為例:
- 搭建燒錄環境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發板的燒錄]
- 搭建開發環境。
- 開始前請參考[工具準備],完成DevEco Studio的安裝和開發環境配置。
- 開發環境配置完成后,請參考[使用工程向導]創建工程(模板選擇“Empty Ability”)。
- 工程創建完成后,選擇使用[真機進行調測]。
代碼結構解讀
本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。
使用Native C++模板創建項目會自動生成cpp文件夾、types文件夾、CMakeList.txt文件,開發者可以根據實際情況自行添加修改其他文件及文件夾。
├──entry/src/main
│ ├──common
│ │ └──CommonContants.ets // 常量定義文件
│ ├──cpp // C++代碼區
│ │ ├──CMakeLists.txt // CMake編譯配置文件
│ │ ├──hello.cpp // C++源代碼
│ │ └──types // 接口存放文件夾
│ │ └──libhello
│ │ ├──index.d.ts // 接口文件
│ │ └──oh-package.json5 // 接口注冊配置文件
│ └──ets // 代碼區
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口類
│ └──pages
│ └──Index.ets // 主界面
└──entry/src/main/resources // 資源文件目錄
架構組成
應用架構
應用架構可以分為三部分:C++、ArkTS、工具鏈。
- C++:包含各種文件的引用、C++或者C代碼、Native項目必需的配置文件等。
- ArkTS:包含界面UI、自身方法、調用引用包的方法等。
- 工具鏈:包含CMake編譯工具在內的系列工具。
使用ArkTS調用C++方法的過程中,需要使用到NAPI、CMake等工具來做中間轉換,整個架構及其關聯關系參考示意圖。
示意圖中,hello.cpp文件實現C++方法,并通過NAPI將C++方法與ArkTS方法關聯。
C++代碼通過CMake編譯工具編譯成動態鏈接庫so文件,使用index.d.ts文件對外提供接口。ArkTS引入so文件后調用其中的接口。
編譯架構
ArkTS與C++方法的調用、編譯流程參考示意圖。圖中C++代碼通過CMake編譯生成so文件后可以直接被ArkTS側引入,最終通過hvigor編譯成可執行的hap包。
Native項目開發流程
Native側操作詳解
配置模塊描述信息,設置Init方法為napi_module的入口方法。 attribute ((constructor))修飾的方法由系統自動調用,使用NAPI接口napi_module_register()傳入模塊描述信息進行模塊注冊。Native C++模板創建項目會自動生成此結代碼,開發者可根據實際情況修改其中內容。
// hello.cpp static napi_module demoModule = { nm_version = 1, nm_flags = 0, nm_filename = nullptr, nm_register_func = Init, // napi_module入口方法 nm_modname = "hello", // napi_module模塊名 nm_priv = ((void *)0), reserved = { 0 } }; extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&demoModule); }
Init方法為Native C++模板生成的結構,開發者可根據實際情況修改其中內容。在napi_property_descriptor desc[]中,我們需要將編寫的MyHypot方法與對外提供的接口myHypot接口進行關聯,其他參數使用示例默認值填寫。使用NAPI接口napi_define_properties構建包含方法對應列表的返回值。
// hello.cpp static napi_value Init(napi_env env, napi_value exports) { if ((nullptr == env) || (nullptr == exports)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null"); return exports; } napi_property_descriptor desc[] = { { "myHypot", nullptr, MyHypot, nullptr, nullptr, nullptr, napi_default, nullptr } }; if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); return nullptr; } return exports; }
本例中使用C標準庫的hypot方法進行計算。引入C標準庫頭文件math.h,使用double類型解析傳入的參數后,調用C標準庫方法hypot計算兩數平方的和后計算平方根。使用NAPI接口napi_create_double將結果轉化為napi_value類型的變量并返回。
// hello.cpp #include < hilog/log.h > #include "napi/native_api.h" #include "math.h" static napi_value MyHypot(napi_env env, napi_callback_info info) { if ((nullptr == env) || (nullptr == info)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "env or exports is null"); return nullptr; } // 參數數量 size_t argc = PARAMETER_COUNT; // 定義參數數組 napi_value args[PARAMETER_COUNT] = { nullptr }; // 獲取傳入的參數并放入參數數組中 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "api_get_cb_info failed"); return nullptr; } // 將傳入的參數轉化為double類型 double valueX = 0.0; double valueY = 0.0; if (napi_ok != napi_get_value_double(env, args[0], &valueX) || napi_ok != napi_get_value_double(env, args[1], &valueY)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_get_value_double failed"); return nullptr; } // 調用C標準庫的hypot接口進行計算 double result = hypot(valueX, valueY); // 創建返回結果并返回 napi_value napiResult; if (napi_ok != napi_create_double(env, result, &napiResult)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_create_double failed"); return nullptr; } return napiResult; }
添加接口文件以及接口配置文件。接口文件index.d.ts用于對外提供方法說明。接口配置文件oh-package.json5文件中將index.d.ts與CMake編譯的so文件關聯起來。模塊級目錄下oh-package.json5文件添加so文件依賴。
// index.d.ts export const myHypot: (a: number, b: number) = > number;
// oh-package.json5 { "name": "libhello.so", "types": "./index.d.ts" } // entry/oh-package.json5 { "devDependencies": { "@types/libhello.so": "file:./src/main/cpp/types/libhello" } }
在CMakeLists.txt文件中配置CMake編譯參數。配置需要添加的hello.cpp文件,編譯后的so文件名為libhello.so。CMakeLists.txt是CMake編譯的配置文件,里面的大部分內容無需修改,project、add_library方法中的內容可以根據實際情況修改。
# CMakeLists.txt # 聲明使用 CMAKE 的最小版本號 cmake_minimum_required(VERSION 3.4.1) # 配置項目信息 project(NativeTemplateDemo) # set命令,格式為set(key value),表示設置key的值為value set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) # 設置頭文件的搜索目錄 include_directories( ${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/include ) # 添加日志庫 find_library( # Sets the name of the path variable. hilog-lib # Specifies the name of the NDK library that # you want CMake to locate. hilog_ndk.z ) # 添加名為hello的庫,庫文件名為libhello.so add_library(hello SHARED hello.cpp) # 添加構建需要鏈接的庫 target_link_libraries(hello PUBLIC ${hilog-lib} libace_napi.z.so libc++.a)
說明:
- CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt文件所在的目錄。
- add_library:添加本地的cpp文件,多cpp文件使用空格或換行間隔。
- target_link_libraries:添加需要鏈接的庫,本篇Codelab使用C標準庫hypot方法,此處鏈接libc++.a庫文件。
ArkTS調用C++方法
Index.ets文件使用import語句導入CMake編譯出的so文件。Button組件添加點擊事件,點擊按鈕觸發點擊事件時,調用libhello.so對外提供的myHypot方法,執行計算并返回計算結果。依據結果值進行格式化,顯示科學計數法或保留指定位小數。
// Index.ets
import libHello from 'libhello.so';
@Entry
@Component
struct Index {
...
build() {
...
Button($r('app.string.submit_button'))
.onClick(() = > {
let resultTemp = libHello.myHypot(this.numX, this.numY);
if (resultTemp > CommonContants.MAX_RESULT) {
this.result = resultTemp.toExponential(CommonContants.EXPONENTIAL_COUNT);
} else {
this.result = resultTemp.toFixed(CommonContants.FIXED_COUNT);
}
})
}
}
界面設計
界面由標題、文本說明、計算結果展示、輸入框、按鈕組成。Index.ets文件完成界面實現,使用Column及Row容器組件進行布局。
// Index.ets
@Entry
@Component
struct NativeTemplate {
...
build() {
Column() {
...
Column() {
...
Row() {
...
TextInput({ controller: this.textInputControllerX })
.type(InputType.Number)
}
.height($r('app.float.tips_num_height'))
.width(CommonContants.FULL_PARENT)
Row() {
...
TextInput({ controller: this.textInputControllerY })
.type(InputType.Number)
.onChange(value = > {
this.numY = parseFloat(value);
})
}
.height($r('app.float.tips_num_height'))
.width(CommonContants.FULL_PARENT)
}
Row() {
Button($r('app.string.submit_button'))
.height(CommonContants.FULL_PARENT)
.width($r('app.float.button_width'))
}
.height($r('app.float.button_height'))
.width(CommonContants.FULL_PARENT)
}
.width(CommonContants.FULL_PARENT)
.height(CommonContants.FULL_PARENT)
}
}
審核編輯 黃宇
-
C++
+關注
關注
22文章
2108瀏覽量
73621 -
OpenHarmony
+關注
關注
25文章
3713瀏覽量
16254 -
鴻蒙OS
+關注
關注
0文章
188瀏覽量
4383
發布評論請先 登錄
相關推薦
評論