鸿蒙应用框架开发【dlopen加载so库并获取Rawfile资源】 NDK

dlopen加载so库并获取Rawfile资源

介绍

本示例中主要介绍在TaskPool子线程中使用dlopen加载so库,以及如何使用Native Rawfile接口操作Rawfile目录和文件。功能包括文件列表遍历、文件打开、搜索、读取和关闭Rawfile。

效果预览

1

使用说明

应用界面中展示了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)

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!

dlopen是POSIX标准中的一部分,它允许程序在运行时动态地加载共享。在Android系统中,这个函数经常被用来加载位于`/vendor/lib64/`目录下的特定的.so共享文件。`/vendor/lib64/`目录通常用于存放特定于设备制造商的本地文件,这些文件是为特定的硬件和设备优化的。 使用dlopen打开并使用`/vendor/lib64/`目录下的.so的过程大致如下: 1. **定义的路径和名称**:首先需要确定要加载的路径和名称,例如`/vendor/lib64/libexample.so`。 2. **调用dlopen函数**:使用`dlopen`函数加载。`dlopen`的第二个参数是一个标志,告诉系统如何处理符号解析和之间的依赖关系。常用的标志有`RTLD_LAZY`和`RTLD_NOW`。 3. **查找符号**:使用`dlsym`函数来查找中定义的符号(通常是函数或者变量)。 4. **使用接口**:一旦获取了符号的地址,就可以像调用本地函数一样使用这些接口了。 5. **卸载**:当不再需要使用时,应使用`dlclose`函数来释放资源并卸载。 下面是一个简单的代码示例,演示了如何使用dlopen打开`/vendor/lib64/`目录下的.so并调用其接口: ```c #include <dlfcn.h> #include <stdio.h> int main() { void* handle; typedef void (*func_t)(); func_t func; // 打开文件 handle = dlopen("/vendor/lib64/libexample.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "Cannot open library: %s\n", dlerror()); return 1; } // 清除之前存在的错误 dlerror(); // 加载符号 func = (func_t)dlsym(handle, "example_function"); const char *dlsym_error = dlerror(); if (dlsym_error) { fprintf(stderr, "Cannot load symbol 'example_function': %s\n", dlsym_error); dlclose(handle); return 1; } // 使用符号 if (func) { func(); // 调用中的函数 } // 卸载 dlclose(handle); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值