一个用于生成napi代码的Python代码仓库
鸿蒙最优雅的NAPI C++ 接口, 使用方便,代码简练,ArkTS接口支持函数、interface,class, 异步处理,,安全函数,多线程、运行时切换等。配合napi_gen 一键生成,可以快速完成NAPI代码。
yyeva动画制作播放的点点滴滴 对对对
https://github.com/getsentry/sentry-dart/tree/8.12.0/flutter/sentry-native
OHOS Utils 是一个为 OpenHarmony 操作系统(OHOS)提供实用功能的 C++ 库。该项目主要聚焦于简化 Native API 的使用,提供日志记录、异步处理、NAPI 封装等功能,适用于构建高性能的 OHOS 应用程序和组件。
NapiHandler,用于更方便地处理 JavaScript 与 Native 代码之间的交互。NapiAsyncHandler 类实现异步任务的创建、执行和结果返回,简化异步编程模型。NapiSafeWrapper封装智能指针,更方便的在c库和ArkTs不同对象之间流转。NapiContext,NapiTaskHandler 可以处理不同线程、不同运行时环境下的任务。NapiScope,帮助开发者管理 Native 资源,避免内存泄漏。OHOS_LOG,支持不同级别的日志输出,便于调试和运行时监控。CMakeLists.txt 文件,确保正确链接本库的源文件。在CMakeLists.txt中增加以下代码
add_definitions(-DOHOS_LOG_TAG=\"mediacodec\")
add_subdirectory(${RELATE_TO_OHOSUTILS_DIR}/ohos_utils ohos_utils)
target_link_libraries(mediacodec PUBLIC ohos_utils)
export class MediaCodec {
constructor(mime: string);
bind(extractor: MediaExtractor, callback: (status: number, codecInfo: CodeInfoAttr) => void): void;
configure(colorFormat?: number, surface?: SurfaceTexture): void;
start(): Promise<void>;
stop(): Promise<void>;
}
// EvaUtils
export const initRender: (controllerId: number, surfaceId: string, isNeedYuv: boolean, isNormalMp4: boolean, isVideoRecord?: boolean) => Promise<number>;
export const setBgBitmap: (controllerId: number, pixelMap: image.PixelMap | null) => void;
对应的c++代码如下:
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
NapiModuleHandler::AddModule("MediaCodec", new MediaCodecNapi(env, exports)); // 类定义
NapiModuleHandler::AddModule("EvaUtils", new EvaUtilsNapi(env, exports)); // 包含多个函数定义
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "mediacodec",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterNapiModule(void)
{
napi_module_register(&demoModule);
}
MediaCodecNapi、EvaUtilsNapi都要继承NapiModuleHandler, 并实现ExportStub函数。具体参考example里的例子
2)一般napi接口处理
type BaseCallback = ()=>void;
export const changeGroupMute:(callback: BaseCallback, operationID: string, groupId: string, isMute: boolean)=>void;
NAPI如下:
static napi_value ChangeGroupMute(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_4);
// 解析参数列表, 分别用INDEX_0, INDEX_1, INDEX_2表示第一、二、三个参数,其他以此类推,目前最大支持INDEX_8
// 结构体类型解析
BaseCallback cb = napiHandler.ParseArgAs<BaseCallback>(INDEX_0, [&](const napi_value &obj) -> BaseCallback {
return ParseBaseCallback(env, obj);
});
// 一般类型解析
std::string operationID = napiHandler.ParseArgAs<std::string>(INDEX_1);
std::string groupID = napiHandler.ParseArgAs<std::string>(INDEX_2);
int32_t isMute = napiHandler.ParseArgAs<int32_t>(INDEX_3);
// 调用c接口
// 返回结果
return napiHandler.GetNapiValue<std::string>();
}
3) 异步函数处理(Promise)
export const start: ()=> Promise<void>;
start的NAPI如下:
static napi_value start(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_0);
std::shared_ptr<MediaCodec> mediaCodec = napiHandler.UnbindSafeObject<MediaCodec>();
if (mediaCodec == nullptr) {
LOGE("Unbind MediaCodec nullptr");
return nullptr;
}
return napiHandler.PromiseCall("start", [mediaCodec](napi_env env, void *d) -> napi_status {
mediaCodec->Start();
return napi_ok;
});
}
export class MediaCodec {
constructor(mime: string);
start(): Promise<void>;
stop(): void;
}
对应的NAPI实现如下:
// 析构函数
static void JsCodecDestructor(napi_env env, void *nativeObject, void *finalizeHint)
{
LOGD("destructor MediaCodec");
NapiHandler napiHandler(env);
napiHandler.DeleteSafeObject<MediaCodec>(nativeObject);
}
// 构造函数
static napi_value JsCodecConstructor(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_0);
LOGD("constructor MediaCodec");
std::shared_ptr<MediaCodec> codec = std::make_shared<MediaCodec>();
return napiHandler.BindSafeObject<MediaCodec>(codec, mediacodec::JsCodecDestructor);
}
// start方法, Promise异步调用
static napi_value start(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_0);
std::shared_ptr<MediaCodec> mediaCodec = napiHandler.UnbindSafeObject<MediaCodec>();
if (mediaCodec == nullptr) {
LOGE("Unbind Extractor nullptr");
return nullptr;
}
return napiHandler.PromiseCall("start", [mediaCodec](napi_env env, void *d) -> napi_status {
mediaCodec->Start();
return napi_ok;
});
}
// stop
static napi_value stop(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_0);
std::shared_ptr<MediaCodec> mediaCodec = napiHandler.UnbindSafeObject<MediaCodec>();
if (mediaCodec == nullptr) {
LOGE("Unbind Extractor nullptr");
return nullptr;
}
mediaCodec->Stop();
return napiHandler.GetVoidValue();
}
void MediaCodecNapi::ExportStub()
{
std::vector<napi_property_descriptor> desc = {
DECLARE_NAPI_FUNCTION("start", start),
DECLARE_NAPI_FUNCTION("stop", stop),
};
ExportClass("MediaCodec", JsCodecConstructor, desc);
}
问题:在使用napi_wrap把两个 C++ 对象包装成两个 JavaScript 对象的场景中,由于这两个 C++ 对象存在依赖关系,要求其中一个C++对象必须在另一个C++对象之前析构。然而,JavaScript 垃圾回收(GC)的时机不确定,直接在napi_wrap的finalize_cb回调里销毁 C++ 对象,没办法保证析构顺序符合要求。该如何保证两个C++对象析构的前后顺序?
我们的解决办法是将一个shared_ptr的智能指针包裹在NapiSafeWrapper内, napi_wrap包裹的是NapiSafeWrapper的指针,每次napi_unwrap 后从NapiSafeWrapper 取出shared_ptr的指针,这样不需要考虑谁先后释放问题。
NapiHandler 提供如下两个方法解决此问题。
// class NapiHandler
template <class T>
napi_value BindSafeObject(const std::shared_ptr<T> &object, napi_finalize destructor)
{
NapiSafeWrapper<T> *wrapper = new NapiSafeWrapper<T>(object);
napi_ref ref = nullptr;
NAPI_CALL(
env_,
napi_wrap(
env_, thisArg_, static_cast<void *>(wrapper), destructor, nullptr, nullptr));
wrapper->SetJsWrapper(env_, ref);
return thisArg_;
}
template <class T>
std::shared_ptr<T> UnbindSafeObject()
{
NapiSafeWrapper<T> *t = nullptr;
NAPI_CALL_HANDLE(env_, napi_unwrap(env_, thisArg_, reinterpret_cast<void **>(&t)), nullptr);
if (t == nullptr) {
return nullptr;
}
return t->GetSafeObject();
}
以下是使用的例子。在构造函数里, 直接将智能指针绑定到NapiSafeWrapper, 而在调用时候,从NapiSafeWrapper取出来。
static napi_value JsCodecConstructor(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_1);
std::string mine = napiHandler.ParseArgAs<std::string>(INDEX_0);
LOGD("constructor MediaCodec");
std::shared_ptr<MediaCodec> codec = std::make_shared<MediaCodec>(mine);
return napiHandler.BindSafeObject<MediaCodec>(codec, [](napi_env env, void *nativeObject, void *finalizeHint) {
LOGD("destructor MediaCodec");
NapiHandler napiHandler(env);
napiHandler.DeleteSafeObject<MediaCodec>(nativeObject);
});
}
static napi_value bind(napi_env env, napi_callback_info info)
{
NapiHandler napiHandler(env, info, PARAM_COUNT_2);
// Note: 具体类型可能处理办法不同
std::shared_ptr<MediaExtractor> extractor = napiHandler.ParseArgAs<std::shared_ptr<MediaExtractor>>(INDEX_0, [&](const napi_env env, const napi_value &obj) -> std::shared_ptr<MediaExtractor> {
NapiSafeWrapper<MediaExtractor> *t = nullptr;
napi_unwrap(env, obj, reinterpret_cast<void **>(&t));
if (t == nullptr) {
return nullptr;
}
return t->GetSafeObject();
});
// Note: Unknown ArkTs type: (status: number, codecInfo: CodeInfoAttr) => void
// TODO: auto callback = napiHandler.ParseArgAs<Unknown ArkTs type>(INDEX_1)
std::shared_ptr<MediaCodec> mediaCodec = napiHandler.UnbindSafeObject<MediaCodec>();
if (mediaCodec == nullptr) {
LOGE("Unbind MediaCodec nullptr");
return nullptr;
}
// TODO: void result = bind(extractor, callback);
return napiHandler.GetVoidValue();
}
欢迎贡献代码和改进文档。请遵循以下步骤提交 PR:
git checkout -b feature/new-feature)。git commit -am 'Add new feature')。git push origin feature/new-feature)。本项目采用 Apache-2.0 许可证。详情请查看 LICENSE 文件。