HarmonyOS NEXT应用开发之预加载so并读取RawFile文件

介绍

本示例主要介绍在TaskPool子线程中使用 dlopen 预加载 so 库并使用句柄调用库函数的方法,以及在Native中使用 pread 系统函数读取Rawfile文件的部分文本内容,并添加 HiLog 日志。

效果图预览

img

使用说明

  1. rawfile路径下存在一个有内容的文本文件rawfile.txt。
  2. 输入开始读取位置、需要读取的长度,点击“开始读取”,即可通过调用Native侧暴露的getRawFileContent接口把读取到的内容显示在界面上。

具体代码可参考MainPage.ets

实现思路

在TaskPool子线程中使用dlopen预加载so库和使用句柄调用so库函数的方式
  1. 将需要加载的.so文件放到工程中,在CMakeLists中使用target_link_directories命令将包含这些库文件的目录添加到预加载库的链接目录。
target_link_directories(preloadso PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/
  1. 使用target_link_libraries命令将需要预加载的so库链接到项目中。
target_link_libraries(preloadso PUBLIC libhilog_ndk.z.so libace_napi.z.so global_handlers libnativerawfile.so)
  1. 定义一个全局对象global_handlers用于存放加载so库所得句柄,其他需要使用global_handlers的cpp文件需要引入定义全局对象的头文件。
std::unordered_map<std::string, void *> global_handlers;
  1. 在 Native 层的 Preload 接口中,遍历传入的 .so 路径数组,使用 dlopen 函数加载库,并将句柄保存到 global_handlers 中。
// 获取传入的so库路径数组的长度
uint32_t arrayLength;
napi_get_array_length(env, args[0], &arrayLength);
for (uint32_t i = 0; i < arrayLength; i++) {
    napi_get_element(env, args[0], i, &pathString); // 获取数组的第 i 个元素
    napi_status status = napi_get_value_string_utf8(env, pathString, path, sizeof(path), &pathLength);
    if (status != napi_ok) {
        // 处理获取路径失败的情况
        continue;
    }
    // TODO:知识点:使用dlopen动态加载so库,返回so库的句柄
    void *handler = dlopen(path, RTLD_LAZY);
    if (handler == nullptr) {
        // TODO:知识点:dlerror抛出加载库失败的错误
        dlerror();
        continue; // 加载下一个
    }
    // 将句柄保存到全局对象global_handlers中
    global_handlers[std::string(path)] = handler;
}
  1. 暴露Preload接口给ArkTS层使用,使其能够通过preload调用Native层的Preload接口。
napi_property_descriptor desc[] = {{"preload", nullptr, Preload, nullptr, nullptr, nullptr, napi_default, nullptr}};
  1. ArkTS层使用TaskPool创建子线程,通过preload接口调用Native侧的Preload接口,实现在TaskPool子线程中加载.so库,导出preloadSOByTaskPool函数。
@Concurrent
function preloadSO(): string[] {
  return napi.preload(Constants.LIBRARY_PATH_ARRAY);
}
export function preloadSOByTaskPool(): void {
  // TODO: 知识点:使用new taskpool.Task()创建任务项,传入任务执行函数和所需参数
  let task: taskpool.Task = new taskpool.Task(preloadSO);
  try {
    // TODO:知识点:使用taskpool.execute将待执行的函数放入TaskPool内部任务队列等待执行
    taskpool.execute(task, taskpool.Priority.HIGH).then((res: string[]) => {
    // so库预加载完成的处理
    logger.info(TAG, '%{public}s', 'PreloadSOByTaskPool:' + JSON.stringify(res));
    })
  } catch (err) {
     logger.error(TAG, "PreloadSOByTaskPool: execute failed, " + (err as BusinessError).toString());
  }
}
  1. 在Ability的onCreate生命周期函数中,调用preloadSOByTaskPool开启子线程,完成so库的预加载。
    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        // 在TaskPool子线程预加载so
        preloadSOByTaskPool();
    }
  1. 后续可以通过句柄使用so库中的函数。
  • 在Native层引入头文件global_handlers.h。
    #include "global\_handlers.h"
  • 编写napi接口,用于实现ArkTS层和so库之间的交互
static napi_value GetTotalRawFileContent(napi_env env, napi_callback_info info){}
static napi_value GetRawFileContent(napi_env env, napi_callback_info info) {}
  • 在napi接口中从全局对象global_handlers中取出对应so库的句柄。
// 从全局对象中获取指定so库的句柄
void *handler = global_handlers["libnativerawfile.so"];
  • 句柄不为空时,使用dlsym查找和调用so库中的符号。
// TODO:知识点:使用dlsym查找和调用so库中的符号
GetTotalRawFileContentWrapperFunc getTotalRawFileContentWrapper =
    reinterpret_cast<GetTotalRawFileContentWrapperFunc>(dlsym(handler, "GetTotalRawFileContentWrapper"));
if (getTotalRawFileContentWrapper) {
    // 调用 GetRawFileContentWrapper 函数
    napi_value result = getTotalRawFileContentWrapper(env, info);
    OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, " GetRawFileContentWrapper finish");
    return result;
} else {
    // 处理无法获取函数指针的情况
    OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, " GetTotalRawFileContentWrapper fn failed");
    return nullptr;
}
  • 在ArkTS层调用编写的napi接口,就可以使用so库导出的函数
this.rawfileContent = nativeRawfileApi.getRawFileContent(getContext().resourceManager, 'rawfile.txt', 2, 5);
Native中加入hilog日志的实现主要步骤如下
  1. 在CMakeLists中通过target_link_libraries导入日志模块libhilog_ndk.z.so。
target_link_libraries(nativerawfile PUBLIC libace_napi.z.so libhilog_ndk.z.so librawfile.z.so)
  1. 在需要打印hilog日志的cpp文件开头引入头文件 #include “hilog/log.h”。
#include "hilog/log.h"
  1. 在需要打印日志的地方通过OH_LOG_Print打印日志。日志级别有LOG_INFO、LOG_ERROR等
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "GetRawFileContent Begin");
Native读取Rawfile中文本文件部分内容主要步骤如下:
  1. 在前端通过调用Native中的getRawFileContent接口读取文件部分内容。传入的参数为文件名、开始读取位置、读取文件长度。
Button($r('app.string.ReadButton'))
  .onClick(()=> {
     this.rawfileContent = nativeRawfileApi.getRawFileContent(getContext().resourceManager, 'rawfile.txt', this.ReadStartPos, this.readLength);
}).margin($r('app.string.rawfile_margin'))
  1. 在Native侧native_rawfile.cpp的getRawFileContent接口中通过Rawfile的API接口以及pread函数读取Rawfile文件部分内容。
 // TODO 知识点:通过pread读取文件部分内容。
 if ((ret = pread(descriptor.fd, buf, lenContent,  descriptor.start + startPos)) == -1) {
     OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "GetRawFileContent pread error!");
 } else {
     buf[lenContent] = '\0';
     OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "GetRawFileContent: %{public}ld: %{public}ld: %{public}s\n",
                  descriptor.start, len, buf);
 }

高性能知识点

不涉及

工程结构&模块类型

nativerawfile                               // har类型
|---libs\
|   |---arm64-v8a\libnativeRawFile.so       // arm64-v8a类型so库
|   |---armeabi-v7a\libnativeRawFile.so     // armeabi-v7a类型so库
|   |---x86_64\libnativeRawFile.so          // x86_64类型so库
|---src\main\ets\components\mainpage\
|   |---MainPage.ets                        // 视图层-Rawfile场景主页面
|---src\main\ets\utils\
|   |---Constants.ets                       // 常量数据
|   |---TaskPool.ets                        // TaskPool子线程加载so库
|---src\main\cpp\
|   |---include\global_handlers.h           // native层-全局句柄头文件
|   |---global_handlers.cpp                 // native层-定义全局句柄对象
|   |---preloadso.cpp                       // native层-加载libnativeRawFile.so业务逻辑
|   |---nativeRawFile.cpp                   // native层-读取Rawfile文件部分内容业务逻辑,libnativeRawFile.so源代码
|   |---native_rawfile_api.cpp              // native层-libnativeRawFile.so和ArkTS中间层接口

模块依赖

  1. 本实例依赖common模块来实现公共组件FunctionDescription

参考资料

公共组件FunctionDescription

最后

随着鸿蒙开发越来越火热,我了解到现在有很多小伙伴想入行鸿蒙,但又不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。我给大家整理了一份实用的鸿蒙(Harmony OS)开发学习手册资料用来跟着学习是非常有利于帮助大家提升鸿蒙开发技术的。

相对于网上那些碎片化的知识内容,这份学习资料的知识点更加系统化,更容易理解和记忆。资料包含了、应用开发导读(ArkTS)、HarmonyOS 概念、如何快速入门、开发基础知识、基于ArkTS 开发、等鸿蒙开发必掌握的核心知识要点,内容包含了(技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

通过这份资料,你将能够系统地学习鸿蒙开发的核心技术,避免在学习的过程中走弯路、浪费时间。相信在不久的将来,你将成为鸿蒙开发领域的佼佼者,为鸿蒙生态的繁荣发展贡献自己的力量。

  • 鸿蒙学习路线图:为您提供一个清晰的鸿蒙学习规划,助您高效掌握关键知识点。

在这里插入图片描述

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的《鸿蒙开发学习笔记》,内容包含ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

【有需要的朋友,可以扫描下方二维码免费领取!!!】

《鸿蒙(HarmonyOS)开发学习指南》

第一章 快速入门

1、开发准备

2、构建第一个ArkTS应用(Stage模型)

3、构建第一个ArkTS应用(FA模型)

4、构建第一个JS应用(FA模型)

5、…

图片

第二章 开发基础知识

1、应用程序包基础知识

2、应用配置文件(Stage模型)

3、应用配置文件概述(FA模型)

4、…

图片

第三章 资源分类与访问

1、 资源分类与访问

2、 创建资源目录和资源文件

3、 资源访问

4、…

图片

第四章 学习ArkTs语言

1、初识ArkTS语言

2、基本语法

3、状态管理

4、其他状态管理

5、渲染控制

6、…

图片

第五章 UI开发

1.方舟开发框架(ArkUI)概述

2.基于ArkTS声明式开发范式

3.兼容JS的类Web开发范式

4…

图片

第六章 Web开发

1.Web组件概述

2.使用Web组件加载页面

3.设置基本属性和事件

4.在应用中使用前端页面JavaScript

5.ArkTS语言基础类库概述

6.并发

7…

图片

11.网络与连接

12.电话服务

13.数据管理

14.文件管理

15.后台任务管理

16.设备管理

17…

图片

第七章 应用模型

1.应用模型概述

2.Stage模型开发指导

3.FA模型开发指导

4…

图片

扫描下方二维码免费领取,《鸿蒙(HarmonyOS)开发学习指南》

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值