dlopen加载so库并获取Rawfile资源
介绍
本示例中主要介绍在TaskPool子线程中使用dlopen加载so库,以及如何使用Native Rawfile接口操作Rawfile目录和文件。功能包括文件列表遍历、文件打开、搜索、读取和关闭Rawfile。
效果预览
使用说明
应用界面中展示了Rawfile相关的接口调用,包括获取resources/rawfile目录下的文件、对应的rawfile文件内容、对应rawfile文件的descriptor。其中使用到的功能包括文件列表遍历、文件打开、搜索、读取和关闭Rawfile。
工程目录
├──entry/libs/ // 需要加载的.so文件
│ ├──arm64-v8a
│ │ └──libentry.so
│ └──x86_64
│ └──libentry.so
├──entry/src/main/cpp/ // native侧代码
│ ├──include
│ │ └──global_handlers.h // 包含全局对象global_handlers的头文件
│ ├──types
│ │ ├──libentry
│ │ │ ├──index.d.ts // 接口导出
│ │ │ └──oh-package.json5
│ │ └──libpreloadso
│ │ ├──index.d.ts // 接口导出
│ │ └──oh-package.json5
│ ├──CMakeLists.txt // 编译脚本
│ ├──global_handlers.cpp // 定义全局对象global_handlers
│ ├──preloadso.cpp // preload接口
│ └──rawfile_demo.cpp // 调用native接口
├──entry/src/main/ets // ArkTS侧代码
│ ├──entryability
│ │ └──EntryAbility.ets
│ ├──pages
│ │ └──Index.ets // 首页
│ └──utils
│ ├──Constants.ets // 预加载的so文件数组
│ ├──Logger.ets // 日志工具
│ └──TaskPool.ets // TaskPool子线程加载so库
└──entry/src/main/resources // 应用静态资源目录
└──rawfile // rawfile资源
├──subrawfile
│ └──rawfile2.txt
├──rawfile.txt
└──rawfile1.txt
具体实现
在TaskPool子线程中使用dlopen加载so库,将需要加载的.so文件放到工程文件libs中,在CMakeLists编译脚本中使用target_link_directories命令将包含这些库文件的目录添加到预加载库的链接目录,使用target_link_libraries命令将需要预加载的so库链接到项目中。 在Native层的Preload接口中,遍历传入的.so路径数组,使用dlopen函数加载库,并将句柄保存到global_handlers中。暴露Preload接口给ArkTS层使用,使其能够通过preload调用Native层的Preload接口。源码参考:[preloadso.cpp]
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "napi/native_api.h"
#include <dlfcn.h>
#include "global_handlers.h"
#include <hilog/log.h>
// Hilog parameters.
const int GLOBAL_RESMGR = 0xFF00;
const char *TAG = "[Preload]";
static napi_value Preload(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "Preload init");
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_value result;
napi_create_array(env, &result); // Create an array to hold the results.
// Args[0] is the SO library path array.
napi_valuetype valuetype;
napi_typeof(env, args[0], &valuetype);
if (valuetype != napi_object) {
return nullptr;
}
// Obtains the length of the input SO library path array.
uint32_t arrayLength;
napi_get_array_length(env, args[0], &arrayLength);
napi_value pathString;
// Path for storing the SO library.
char path[256];
size_t pathLength;
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "Preload start");
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, args[0], i, &pathString);
napi_status status = napi_get_value_string_utf8(env, pathString, path, sizeof(path), &pathLength);
if (status != napi_ok) {
// Processing the Failure of Obtaining the Path.
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, " Preload failed");
continue;
}
// Use dlopen to dynamically load the SO library and return the handle of the SO library.
void *handler = dlopen(path, RTLD_LAZY);
if (handler == nullptr) {
// Dlerror throws failure to load library.
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, " Preload failed");
dlerror();
continue;
}
// Save the handles to the global object global_handlers.
global_handlers[std::string(path)] = handler;
// Add the path of the successfully loaded library to the result array.
napi_set_element(env, result, i, pathString);
}
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "Preload finish");
// Return the result array that is loaded successfully.
return result;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {{"preload", nullptr, Preload, 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 = "preloadso",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterPreloadsoModule(void) { napi_module_register(&demoModule); }
ArkTS层使用TaskPool创建子线程,通过preload接口调用Native侧的Preload接口,实现在TaskPool子线程中加载.so库,导出preloadSOByTaskPool函数。 在Ability的onCreate生命周期函数中,调用preloadSOByTaskPool开启子线程,完成so库的预加载。源码参考:[TaskPool.ets]
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { taskpool } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import napi from 'libpreloadso.so';
import Constants from './Constants';
import { Logger } from './Logger';
/**
* Enable the taskpool subthread to preload .so files.
* @param
* @returns string[] Loaded so library.
*/
@Concurrent
function preloadSO(): string[] {
return napi.preload(Constants.LIBRARY_PATH_ARRAY);
}
/**
* Starts the taskpool sub-thread.
* @param
* @returns Promise<string[]> Asynchronously return the loaded so library.
*/
export function preloadSOByTaskPool(): void {
// Use new taskpool.Task () to create a task item and input the task execution function and required parameters.
const task: taskpool.Task = new taskpool.Task(preloadSO);
try {
// Use taskpool.execute to put the function to be executed into the internal task queue of TaskPool for execution.
taskpool.execute(task, taskpool.Priority.HIGH)
.then((res: Object) => {
// Processing after the so library is preloaded.
Logger.info('PreloadSOByTaskPool:' + JSON.stringify(res));
})
.catch((error: BusinessError) => {
Logger.error('PreloadSOByTaskPool: execute failed, ' + error.toString());
});
} catch (err) {
Logger.error('PreloadSOByTaskPool: execute failed, ' + (err as BusinessError).toString());
}
}
在Native层定义对外接口为getFileList、getRawFileContent、getRawFileDescriptor,映射C++接口分别为GetFileList、GetRawFileContent、GetRawFileDescriptor。 通过获取Js的资源对象,并转为Native的资源对象,即可调用资源的Native接口,获取rawfile列表、rawfile文件内容以及rawfile描述符{fd, offset, length}。 在Js侧导入"libentry.so",通过getContext().resourceManager获取资源管理对象。调用src/main/cpp/types/libentry/index.d.ts中声明的接口,传入js的资源对象和相关参数获取对于rawfile相关资源信息。 源码参考:[rawfile_demo.cpp] 。
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <vector>
#include "napi/native_api.h"
#include "rawfile/raw_file_manager.h"
#include "rawfile/raw_file.h"
#include "rawfile/raw_dir.h"
#include "hilog/log.h"
const int GLOBAL_RESMGR = 0xFF00;
const char *TAG = "[Sample_rawfile]";
static napi_value GetFileList(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "GetFileList Begin");
size_t argc = 2;
napi_value argv[2] = {nullptr};
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
napi_valuetype valueType;
napi_typeof(env, argv[0], &valueType);
NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
size_t strSize;
char strBuf[256];
napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
std::string filename(strBuf, strSize);
RawDir *rawDir = OH_ResourceManager_OpenRawDir(mNativeResMgr, filename.c_str());
int count = OH_ResourceManager_GetRawFileCount(rawDir);
std::vector<std::string> tempArray;
for (int i = 0; i < count; i++) {
std::string filename = OH_ResourceManager_GetRawFileName(rawDir, i);
tempArray.emplace_back(filename);
}
napi_value fileList;
napi_create_array(env, &fileList);
for (size_t i = 0; i < tempArray.size(); i++) {
napi_value jsString;
napi_create_string_utf8(env, tempArray[i].c_str(), NAPI_AUTO_LENGTH, &jsString);
napi_set_element(env, fileList, i, jsString);
}
OH_ResourceManager_CloseRawDir(rawDir);
OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
return fileList;
}
namespace {
napi_value CreateJsArrayValue(napi_env env, std::unique_ptr<uint8_t[]> &data, long length) {
napi_value buffer;
napi_status status = napi_create_external_arraybuffer(
env, data.get(), length, [](napi_env env, void *data, void *hint) { delete[] static_cast<char *>(data); },
nullptr, &buffer);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "Failed to create external array buffer");
return nullptr;
}
napi_value result = nullptr;
status = napi_create_typedarray(env, napi_uint8_array, length, buffer, 0, &result);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "Failed to create media typed array");
return nullptr;
}
data.release();
return result;
}
} // namespace
static napi_value GetRawFileContent(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "GetFileContent Begin");
size_t argc = 2;
napi_value argv[2] = {nullptr};
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
napi_valuetype valueType;
napi_typeof(env, argv[0], &valueType);
NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
size_t strSize;
char strBuf[256];
napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
std::string filename(strBuf, strSize);
RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, filename.c_str());
if (rawFile != nullptr) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "OH_ResourceManager_OpenRawFile success");
}
long len = OH_ResourceManager_GetRawFileSize(rawFile);
std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(len);
int res = OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
OH_ResourceManager_CloseRawFile(rawFile);
OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
return CreateJsArrayValue(env, data, len);
}
namespace {
napi_value createJsFileDescriptor(napi_env env, RawFileDescriptor &descriptor) {
napi_value result;
napi_status status = napi_create_object(env, &result);
if (status != napi_ok) {
return result;
}
napi_value fd;
status = napi_create_int32(env, descriptor.fd, &fd);
if (status != napi_ok) {
return result;
}
status = napi_set_named_property(env, result, "fd", fd);
if (status != napi_ok) {
return result;
}
napi_value offset;
status = napi_create_int64(env, descriptor.start, &offset);
if (status != napi_ok) {
return result;
}
status = napi_set_named_property(env, result, "offset", offset);
if (status != napi_ok) {
return result;
}
napi_value length;
status = napi_create_int64(env, descriptor.length, &length);
if (status != napi_ok) {
return result;
}
status = napi_set_named_property(env, result, "length", length);
if (status != napi_ok) {
return result;
}
return result;
}
} // namespace
static napi_value GetRawFileDescriptor(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "GetRawFileDescriptor Begin");
size_t argc = 2;
napi_value argv[2] = {nullptr};
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
napi_valuetype valueType;
napi_typeof(env, argv[0], &valueType);
NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
size_t strSize;
char strBuf[256];
napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
std::string filename(strBuf, strSize);
RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, filename.c_str());
if (rawFile != nullptr) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "OH_ResourceManager_OpenRawFile success");
}
RawFileDescriptor descriptor;
OH_ResourceManager_GetRawFileDescriptor(rawFile, descriptor);
OH_ResourceManager_CloseRawFile(rawFile);
OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
return createJsFileDescriptor(env, descriptor);
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"getFileList", nullptr, GetFileList, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getRawFileContent", nullptr, GetRawFileContent, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getRawFileDescriptor", nullptr, GetRawFileDescriptor, 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); }
涉及到的相关接口:
接口名 | 描述 |
---|---|
初始化native resource manager。 | NativeResourceManager *OH_ResourceManager_InitNativeResourceManager(napi_env env, napi_value jsResMgr) |
打开指定rawfile目录。 | RawDir *OH_ResourceManager_OpenRawDir(const NativeResourceManager *mgr, const char *dirName) |
获取指定rawfile目录下的rawfile文件数量。 | int OH_ResourceManager_GetRawFileCount(RawDir *rawDir) |
获取rawfile名字。 | const char *OH_ResourceManager_GetRawFileName(RawDir *rawDir, int index) |
打开指定rawfile文件。 | RawFile *OH_ResourceManager_OpenRawFile(const NativeResourceManager *mgr, const char *fileName) |
获取rawfile文件大小。 | long OH_ResourceManager_GetRawFileSize(RawFile *rawFile) |
读取rawfile文件内容。 | int OH_ResourceManager_ReadRawFile(const RawFile *rawFile, void *buf, size_t length) |
释放rawfile文件相关资源。 | void OH_ResourceManager_CloseRawFile(RawFile *rawFile) |
释放rawfile目录相关资源。 | void OH_ResourceManager_CloseRawDir(RawDir *rawDir) |
获取rawfile的fd。 | bool OH_ResourceManager_GetRawFileDescriptor(const RawFile *rawFile, RawFileDescriptor &descriptor) |
释放native resource manager相关资源。 | void OH_ResourceManager_ReleaseNativeResourceManager(NativeResourceManager *resMgr) |
以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下:
内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!
鸿蒙【北向应用开发+南向系统层开发】文档
鸿蒙【基础+实战项目】视频
鸿蒙面经
为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!