前言:![]()
鸿蒙(HarmonyOS)的NDK(Native Development Kit)开发允许开发者使用C/C++编写高性能代码,并与Java/JS层进行交互。
首先:
创建Native项目
-
选择模板
新建项目时,选择Native C++模板,系统会自动生成Native层代码结构(包括cpp目录和CMakeLists.txt)。
-
项目结构说明
编写Native代码
-
实现JNI接口
在Java/JS层声明Native方法(如public native String helloFromJNI()),并通过native-lib.cpp实现对应的JNI函数(需遵循JNIEXPORT和JNICALL规范)。
-
跨语言交互
编译与构建
-
配置CMake
在CMakeLists.txt中指定生成的动态库名称(如add_library(native-lib SHARED native-lib.cpp)),并关联系统库(如libace_napi.z.so)。
-
构建HAP包
执行Build > Build Hap(s),生成包含Native库(.so文件)的HAP安装包。构建日志中可检查NDK编译是否成功。
项目目录:
![]()
├──entry/src/main/cpp // cpp代码区
│ ├──thirdparty
│ │ ├──libarchive // 32位和64位三方库
│ │ │ ├──arm64-v8a
│ │ │ └──armeabi-v7a
│ │ ├──xz // 32位和64位文件
│ │ │ ├──arm64-v8a
│ │ │ └──armeabi-v7a
│ │ └───zstd // 32位和64位文件
│ │ ├──arm64-v8a
│ │ └──armeabi-v7a
│ │
│ ├──types/mycompress // 自定义接口
│ │ │────Index.d.ts
│ │ └──oh-package.json5
│ │────CMakeLists.txt // CMake配置
│ │────napi_init.cpp // c方法实现
│ ├──ets
│ │ └──CompressLib.ets // arkts方法转换
│ │
│ └────resources/base/element // 应用资源目录
│
└──entry/src/main/resources
├──entry/src/main/ets // ets代码区
│ ├──entryability
│ │ └──EntryAbility.ets
│ ├──entrybackupability
│ │ └──EntryBackupAbility.ets
│ └──pages // UI入口
│ └──Index.ets
└──entry/src/main/resources // 应用资源目录
接下来我们来介绍一下项目代码:
1.在CMakeLists.txt文件配置如下:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(mycompress)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(mycompress SHARED napi_init.cpp)
target_link_libraries(mycompress PUBLIC libace_napi.z.so libhilog_ndk.z.so)
target_link_libraries(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libarchive/${OHOS_ARCH}/lib/libarchive.so)
target_link_libraries(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/xz/${OHOS_ARCH}/lib/liblzma.so)
target_link_libraries(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/zstd/${OHOS_ARCH}/lib/libzstd.so)
target_include_directories(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libarchive/${OHOS_ARCH}/include)
target_include_directories(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/xz/${OHOS_ARCH}/include)
target_include_directories(mycompress PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/zstd/${OHOS_ARCH}/include)
2.在Index.d.ts文件中配置如下:
export const decompress: (inFile: string, outFile: string) => object
export const compress: (inFile: string, outFile: string, format: string) => object
export const getFileList: (path: string,outFile: string,) => object
export const getFileData: (path: string,outFile: string,) => object
3.在napi_init.cpp文件里面进行功能方法实现:
#include
#include
#include
#include
#include
#include
4.最后在CompressLib.ets文件里面调用:
import mycompress from 'libmyarchive.so'
export namespace CompressLib {
export interface Result {
code: number
message: string
}
export type CompressFormat = '7z' | 'tar' | 'zip' | 'gz' | 'xz'| 'rar'
export async function decompress(inFile: string, outFile: string): Promise {
return mycompress.decompress(inFile, outFile) as Result
}
export async function compress(inFile: string, outFile: string, format: CompressFormat): Promise {
return mycompress.compress(inFile, outFile, format) as Result
}
export async function getFileList(inFile: string,outFile: string): Promise {
return mycompress.getFileList(inFile,outFile) as Result
}
export async function getFileData(inFile: string,outFile: string): Promise {
return mycompress.getFileData(inFile,outFile) as Result
}
}
项目的UI文件用简单的ets编写:
在oh-package.json5配置依赖
"dependencies": {
"@mycompress/compress": "file:../compress"
}
在page/Index.ets里面使用:
import { fileIo as fs, ListFileOptions } from '@kit.CoreFileKit'
import { CompressLib } from '@mycompress/compress'
@Entry
@Component
struct Index {
private readonly context = getContext()
private readonly inFile = `${this.context.tempDir}/src`
//
private readonly formats: CompressLib.CompressFormat[] = ['7z', 'tar', 'zip', 'gz', 'xz','rar']
@State format: CompressLib.CompressFormat = '7z'
private outFile:string = `${this.context.filesDir}/compress`
async aboutToAppear() {
let curDir = this.inFile
for (let i = 0; i < 5; i++) {
if (!fs.accessSync(curDir)) {
await fs.mkdir(curDir, true)
}
const filepath = `${curDir}/${i}.txt`
if (!fs.accessSync(filepath)) {
const f = await fs.open(filepath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY)
fs.write(f.fd, `test_${i}\ntest_${i}\ntest_${i}`)
fs.close(f)
}
curDir += `/${i}`
}
}
build() {
Column({space:10}) {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
ForEach(this.formats, (format: CompressLib.CompressFormat) => {
Column() {
Text(format)
Radio({ value: format, group: 'radioGroup' })
.width(25)
.height(25)
.checked(this.format === format)
.onChange((isChecked: boolean) => {
if (isChecked) {
this.format = format
}
})
}
})
}.padding({ top: 30 })
Column(){
Text(`>>>>>>文件Test.txt<<<<<<`)
.width('90%')
.textAlign(TextAlign.Center)
Button('压缩').width('90%')
.onClick(async ()=>{
const ret = await CompressLib.compress(this.inFile, this.outFile, this.format)
if (ret.code === 0) {
console.log('压缩成功')
} else {
console.log(`压缩失败,code: ${ret.code}, message: ${ret.message}`)
}
})
}.layoutWeight(1)
.justifyContent(FlexAlign.SpaceAround)
Column(){
Text(`>>>>>>文件Test.zip<<<<<<`)
.width('90%')
.textAlign(TextAlign.Center)
Button('获取列表').width('90%')
.onClick(async ()=>{
const ret = await CompressLib.getFileList(this.inFile, this.outFile)
if (ret.code === 0) {
console.log('压缩成功')
} else {
console.log(`压缩失败,code: ${ret.code}, message: ${ret.message}`)
}
})
}.layoutWeight(1)
.justifyContent(FlexAlign.SpaceAround)
Column(){
Text(`>>>>>>文件Test.zip<<<<<<`)
.width('90%')
.textAlign(TextAlign.Center)
Button('获取单个文件').width('90%')
.onClick(async ()=>{
const ret = await CompressLib.getFileData(this.inFile, this.outFile)
if (ret.code === 0) {
console.log('压缩成功')
} else {
console.log(`压缩失败,code: ${ret.code}, message: ${ret.message}`)
}
})
}.layoutWeight(1)
.justifyContent(FlexAlign.SpaceAround)
Column(){
Text(`>>>>>>文件Test.zip<<<<<<`)
.width('90%')
.textAlign(TextAlign.Center)
Button('解压').width('90%')
.onClick(async ()=>{
const ret = await CompressLib.decompress(this.inFile, this.outFile)
if (ret.code === 0) {
console.log('压缩成功')
} else {
console.log(`压缩失败,code: ${ret.code}, message: ${ret.message}`)
}
})
}.layoutWeight(1)
.justifyContent(FlexAlign.SpaceAround)
}
.justifyContent(FlexAlign.Start)
.height('100%')
.width('100%')
}
}
综上,一个基本的NDK实现解压缩功能就完成了。
Demo链接:Stevenllv/compress_demo
学习路径如下:
1. 鸿蒙-ArkTS和Native之间的交互使用1——创建NDK工程-CSDN博客
2.鸿蒙-ArkTS和Native之间的交互使用2——如何调用Napi-CSDN博客
3.鸿蒙-ArkTS和Native之间的交互使用3——使用Node-API接口创建基本数据类型-CSDN博客
4.鸿蒙-ArkTS和Native之间的交互使用4——使用Node-API接口进行object相关开发-CSDN博客
5.鸿蒙-ArkTS和Native之间的交互使用5——使用原生Zip模块开发解压缩功能-CSDN博客
6.鸿蒙-ArkTS和Native之间的交互使用6——使用三方库jszip开发解压缩功能-CSDN博客
7.鸿蒙-ArkTS和Native之间的交互使用7——使用三方库 libarchive开发解压缩功能-CSDN博客
8.鸿蒙-ArkTS和Native之间的交互使用8——使用 libarchive创建compress的napi压缩接口-CSDN博客
9.鸿蒙-ArkTS和Native之间的交互使用9——使用 libarchive创建Decompress的napi压缩接口-CSDN博客
10.鸿蒙-ArkTS和Native之间的交互使用10——使用 libarchive创建getCompressList的napi压缩接口-CSDN博客
11.鸿蒙-ArkTS和Native之间的交互使用11——使用 libarchive创建getExtractFile的napi压缩接口-CSDN博客