openharmony 鸿蒙 content-embed-server-guidelines

2026-08-25 浏览 (1)

服务端应用开发

场景介绍

OH_ContentEmbed内容嵌入模块提供对象编辑框架与技术,支持应用间文档嵌入与协同编辑。

OE服务端应用使用OE Extension框架提供的接口,向客户端应用提供特定格式文档的嵌入与编辑能力。

约束限制

  • 使用此接口,需确认设备具有以下系统能力:SystemCapability.ContentEmbed.ObjectEditor。
  • 申请权限:开发者需要申请ohos.permission.REGISTER_OBJECTEDITOR_EXTENSION权限,配置方式请参阅声明权限

接口说明

常用接口如下表所示。更多API说明请参考OH_ContentEmbed

表1 服务端主要接口

接口名称功能描述
OH_ContentEmbed_Extension_GetExtensionInstance从ExtensionAbility基类实例中获取对应的OE Extension实例。
OH_ContentEmbed_Extension_GetContentEmbedContext从OE Extension实例中获取其对应的OE Extension上下文对象。
OH_ContentEmbed_Extension_GetContext从OE Extension上下文中获取AbilityRuntime上下文。
OH_ContentEmbed_Extension_RegisterOnCreateFunc注册OE Extension实例创建时的生命周期函数。
OH_ContentEmbed_Extension_RegisterOnDestroyFunc注册OE Extension实例销毁时的生命周期函数。
OH_ContentEmbed_Extension_RegisterOnObjectAttachFunc注册客户端OE对象连接时的回调函数。
OH_ContentEmbed_Extension_RegisterOnObjectDetachFunc取消注册客户端OE对象连接时的回调函数。
OH_ContentEmbed_Extension_RegisterOnWriteToDataStreamFunc注册服务端OE对象写入OE文档数据流时的回调函数。
OH_ContentEmbed_Extension_RegisterOnGetSnapshotFunc注册客户端OE对象请求获取OE文档快照时的回调函数。
OH_ContentEmbed_Extension_RegisterOnDoEditFunc注册客户端OE对象请求编辑OE文档时的回调函数。
OH_ContentEmbed_Extension_RegisterOnGetEditStatusFunc注册客户端OE对象请求OE文档编辑状态时的回调函数。
OH_ContentEmbed_Extension_RegisterOnGetCapabilityFunc注册客户端OE对象查询OE Extension实例支持能力时的回调函数。
OH_ContentEmbed_Extension_GetContentEmbedDocument获取服务端OE对象关联的OE文档实例。
OH_ContentEmbed_Extension_CallbackToOnUpdate触发客户端OE对象注册的OE文档更新回调函数。
OH_ContentEmbed_Extension_CallbackToOnError触发客户端OE对象注册的OE文档错误回调函数。
OH_ContentEmbed_Extension_CallbackToOnEditingFinished触发客户端OE对象注册的OE文档编辑完成回调函数。
OH_ContentEmbed_Extension_CallbackToOnExtensionStopped触发OE Extension关联的所有客户端OE对象注册的OE Extension停止时的回调函数。
OH_ContentEmbed_Extension_SetSnapshot设置客户端OE对象关联的OE文档快照图像。
OH_ContentEmbed_Extension_ContextStartSelfUIAbility通过OE Extension上下文启动自身的UIAbility。

开发步骤

以下演示使用Native API开发OE服务端应用的完整流程,演示了OE服务端注册Extension组件回调,响应客户端请求,返回OE文档快照和启动UIAbility编辑OE文档。

OE服务端配置OE ExtensionAbility

从API version 24版本开始,在module.json5文件的extensionAbilities标签中配置OE ExtensionAbility,示例如下:

"extensionAbilities": [
    {
        "name": "OEExtAbility",
        "srcEntry": "libentry.so",
        "type": "contentEmbed",
        "exported": true,
        "metadata": [
            {
                "name": "content_embed_config",
                "resource": "$profile:content_embed_config"
            }
        ]
    }
]

配置说明:

  • name:Extension组件的名称。
  • srcEntry:Extension组件的入口库文件路径。
  • type:必须设置为"contentEmbed"。
  • exported:必须设置为true,表示对外暴露。
  • metadata:元数据信息"metadata"新增一个"name"为"content_embed_config"的数据项,"resource"为OE Extension配置文件的资源索引。

开发者需要新增一个二级配置json文件,用于配置OE Extension信息。例如将"resource"配置成"$profile:content_embed_config",表示指向resources/base/profile/content_embed_config.json配置文件。json文件示例如下:

{
    "content_embed_config": [
        {
            "oeid": "E0A8B74A-445B-4D7A-8B05-07B5509B50D8",
            "file_exts": ".doc|.docx",
            "icon": "$media:app_logo",
            "name": "$string:name",
            "description": "$string:description"
        }
    ]
}

配置说明:

  • oeid:OE文档的系统可识别标识符,用于定位支持该OE文档的OE服务端应用。
    • 如果OE服务端应用在其它操作系统上也有相同功能,建议复用已在其它系统中使用的ID;
    • 如果OE服务端应用该功能仅在OpenHarmony系统上提供,则建议使用系统自带的“终端”工具,通过uuidgen命令生成新的ID。
  • file_exts:支持的文件扩展名,如".doc"、".docx"等,如果支持多个文件后缀,使用"|"进行隔开。
  • icon:OE文档查询显示图标,取值为图标资源文件的索引。
  • name:OE文档查询显示名称,要求采用该名称的资源索引,以支持多语言。
  • description:OE文档查询显示描述,要求采用该名称的资源索引,以支持多语言。

添加动态链接库

CMakeLists.txt中添加以下lib。

# content embed
libcontent_embed_ndk.so
# hilog
libhilog_ndk.z.so
# ace
libace_napi.z.so
# piexlmap
libpixelmap.so
# ability
libability_runtime.so
# want
libability_base_want.so
# fileuri
libohfileuri.so
# libimage_source
libimage_source.so

导入头文件

#include <cstdint>
#include <fstream>
#include <hilog/log.h>
#include <AbilityKit/ability_base/want.h>
#include <AbilityKit/ability_runtime/application_context.h>
#include <ContentEmbedKit/content_embed/content_embed_document.h>
#include <ContentEmbedKit/content_embed/content_embed_extension.h>
#include <filemanagement/file_uri/oh_file_uri.h>
#include <multimedia/image_framework/image/image_source_native.h>

定义全局变量

static ContentEmbed_ExtensionInstanceHandle g_instance = nullptr;

注册Extension回调函数

当OE服务端应用的OE Extension被系统启动以响应OE客户端请求时,首先执行OH_AbilityRuntime_OnNativeExtensionCreate函数,需在该函数中注册OE Extension回调,以响应客户端请求。

extern "C" void OH_AbilityRuntime_OnNativeExtensionCreate(AbilityRuntime_ExtensionInstance *instance, const char *abilityName) {
    if (instance == nullptr) {
        OH_LOG_ERROR(LOG_APP, "instance is null");
        return;
    }

    ContentEmbed_ExtensionInstanceHandle ceExtensionInstance;
    // 获取OE Extension实例
    ContentEmbed_ErrorCode ret = OH_ContentEmbed_Extension_GetExtensionInstance(instance, &ceExtensionInstance);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_GetExtensionInstance failed, errCode: %{public}d.", ret);
        return;
    }
    // 给全局变量赋值
    g_instance = ceExtensionInstance;
    // 注册OE Extension创建函数回调
    ret = OH_ContentEmbed_Extension_RegisterOnCreateFunc(ceExtensionInstance, NativeOnCreate);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnCreateFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // 注册OE Extension销毁函数回调
    ret = OH_ContentEmbed_Extension_RegisterOnDestroyFunc(ceExtensionInstance, NativeOnDestroy);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnDestroyFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // 注册服务端OE对象绑定回调
    ret = OH_ContentEmbed_Extension_RegisterOnObjectAttachFunc(ceExtensionInstance, RegisterOnObjectAttachFunc);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnObjectAttachFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // 注册服务端OE对象解绑回调
    ret = OH_ContentEmbed_Extension_RegisterOnObjectDetachFunc(ceExtensionInstance, RegisterOnObjectDetachFunc);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnObjectDetachFunc failed, errCode: %{public}d.", ret);
        return;
    }
}

实现创建和销毁生命周期回调函数

static void NativeOnCreate(ContentEmbed_ExtensionInstanceHandle instance, AbilityBase_Want *want)
{
    OH_LOG_INFO(LOG_APP, "enter NativeOnCreate");
}

static void NativeOnDestroy(ContentEmbed_ExtensionInstanceHandle instance)
{
    OH_LOG_INFO(LOG_APP, "enter NativeOnDestroy");
}

实现服务端OE对象绑定和解绑的回调函数

当OE客户端通过OH_ContentEmbed_Proxy_StartWork函数将客户端OE对象与服务端OE对象绑定时,会触发OE服务端的RegisterOnObjectAttachFunc 回调,在该回调中OE服务端需调用服务端OE对象的注册函数以响应OE客户端的请求。

当OE客户端通过OH_ContentEmbed_Proxy_StopWork函数将客户端OE对象与服务端OE对象解除绑定时,会触发OE服务端的RegisterOnObjectDetachFunc 回调,在该回调后服务端OE对象将失效。

static void RegisterOnObjectAttachFunc(ContentEmbed_ExtensionInstanceHandle instance, ContentEmbed_ObjectHandle object)
{
    ContentEmbed_ErrorCode ret = CE_ERR_OK;
    // 当OE文档是通过文件创建的时候执行该回调函数
    ret = OH_ContentEmbed_Extension_RegisterOnWriteToDataStreamFunc(object, NativeOnWriteToDataStream);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnWriteToDataStreamFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // 获取当前OE Extension的能力
    ret = OH_ContentEmbed_Extension_RegisterOnGetCapabilityFunc(object, NativeOnGetCapability);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnGetCapabilityFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // OE文档触发编辑操作
    ret = OH_ContentEmbed_Extension_RegisterOnDoEditFunc(object, NativeOnDoEdit);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnDoEditFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // OE文档请求获取快照
    ret = OH_ContentEmbed_Extension_RegisterOnGetSnapshotFunc(object, NativeOnGetSnapshot);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnGetSnapshotFunc failed, errCode: %{public}d.", ret);
        return;
    }
    // OE文档查询当前编辑状态
    ret = OH_ContentEmbed_Extension_RegisterOnGetEditStatusFunc(object, NativeOnGetEditStatusFunc);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_RegisterOnGetEditStatusFunc failed, errCode: %{public}d.", ret);
        return;
    }
}

static void RegisterOnObjectDetachFunc(ContentEmbed_ExtensionInstanceHandle instance, ContentEmbed_ObjectHandle object)
{
    OH_LOG_INFO(LOG_APP, "enter RegisterOnObjectDetachFunc");
}

实现获取OE文档快照

当OE客户端通过新建对象类型或已存在文件来嵌入OE对象时,OE对象在OE客户端界面中可能呈现为文档快照(Snapshot),当OE Extension被启动后,OE客户端会通过OH_ContentEmbed_Proxy_GetSnapshot获取文档快照,此时会触发OE服务端的NativeOnGetSnapshot回调,在该回调中OE服务端应用需调用OH_ContentEmbed_Extension_SetSnapshot设置OE文档快照。

static void NativeOnGetSnapshot(ContentEmbed_ObjectHandle object)
{
    // 当前OE文档快照路径
    std::string path = "/data/storage/el2/base/haps/entry/temp/web_snapshot.png";
    // 判断当前是否有UI生成的缩略图
    FILE *srcFile = fopen(path.c_str(), "rb");
    if (!srcFile) {
        OH_LOG_ERROR(LOG_APP, "文件打开失败");
        return;
    }
    int fd = fileno(srcFile);
    // 创建ImageSource实例。
    OH_ImageSourceNative *source = nullptr;
    Image_ErrorCode errCode = OH_ImageSourceNative_CreateFromFd(fd, &source);
    if (errCode != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "OH_ImageSourceNative_CreateFromFd failed, errCode: %{public}d.", errCode);
        return;
    }
    // 通过图片解码参数创建PixelMap对象。
    OH_DecodingOptions *ops = nullptr;
    OH_DecodingOptions_Create(&ops);
    // 设置为AUTO会根据图片资源格式解码,如果图片资源为HDR资源则会解码为HDR的pixelmap。
    OH_DecodingOptions_SetDesiredDynamicRange(ops, IMAGE_DYNAMIC_RANGE_AUTO);
    OH_PixelmapNative *resPixMap = nullptr;

    // ops参数支持传入nullptr, 当不需要设置解码参数时,不用创建。
    errCode = OH_ImageSourceNative_CreatePixelmap(source, ops, &resPixMap);
    OH_DecodingOptions_Release(ops);
    if (errCode != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "OH_ImageSourceNative_CreatePixelmap failed, errCode: %{public}d.", errCode);
        return;
    }
    // 设置快照
    ContentEmbed_ErrorCode ret = OH_ContentEmbed_Extension_SetSnapshot(object, resPixMap);
    // 释放ImageSource实例。
    OH_ImageSourceNative_Release(source);
}

实现编辑OE文档

当OE Extension被启动后,OE客户端会通过OH_ContentEmbed_Proxy_DoEdit通知OE服务端编辑OE文档,此时会触发OE服务端的NativeOnDoEdit回调,在该回调中OE服务端应用需调用OH_ContentEmbed_Extension_ContextStartSelfUIAbilityOH_ContentEmbed_Extension_ContextStartSelfUIAbilityWithStartOptions启动OE服务端应用的UIAbility编辑文档。

static void NativeOnDoEdit(ContentEmbed_ObjectHandle object)
{
    ContentEmbed_ErrorCode ret = CE_ERR_OK;
    ContentEmbed_Document* ceDocument;
    // 获取当前OE文档
    ret = OH_ContentEmbed_Extension_GetContentEmbedDocument(object, &ceDocument);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_GetContentEmbedDocument failed, errCode: %{public}d.", ret);
        return;
    }
    // 获取当前上下文实例
    ContentEmbed_ExtensionContextHandle context;
    ret = OH_ContentEmbed_Extension_GetContentEmbedContext(g_instance, &context);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_GetContentEmbedContext failed, errCode: %{public}d.", ret);
        return;
    }
    // 需要启动的UIAbility的信息
    char* bundleName = "com.example.oeserverdemo"; // 服务端应用的包名
    char* moduleName = "entry";
    char* abilityName = "EntryAbility";
    // 创建Want对象
    AbilityBase_Element element = {
        .bundleName = bundleName,
        .moduleName = moduleName,
        .abilityName = abilityName,
    };
    AbilityBase_Want* want = OH_AbilityBase_CreateWant(element);
    // 获取OE文档的RootStorage
    ContentEmbed_Storage *rootStorage = nullptr;
    ret = OH_ContentEmbed_Document_GetRootStorage(ceDocument, &rootStorage);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Document_GetRootStorage failed, errCode: %{public}d.", ret);
        return;
    }
    // 判断document中是否有指定stream
    ContentEmbed_Stream *childStream;
    char* fileType = "aaa";
    ret = OH_ContentEmbed_Storage_GetStream(rootStorage, fileType, &childStream);
    if (ret == CE_ERR_OK) {
        size_t bufferSize;
        // 获取OE文档流的大小
        ret = OH_ContentEmbed_Stream_GetSize(childStream, &bufferSize);
        if (ret != CE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Stream_GetSize failed, errCode: %{public}d.", ret);
            return;
        }
        // Read之前需要将流位置重置
        ret = OH_ContentEmbed_Stream_Seek(childStream, 0);
        if (ret != CE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Stream_Seek failed, errCode: %{public}d.", ret);
            return;
        }
        unsigned char *buffer;
        size_t num = 0;
        // 读取流数据
        ret = OH_ContentEmbed_Stream_Read(childStream, &buffer, bufferSize, &num);
        if (ret != CE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Stream_Read failed, errCode: %{public}d.", ret);
            return;
        }
        std::string tempPath = "/data/storage/el2/base/cache/temp.aaa";
        // 创建输出文件流对象,并以二进制模式打开文件
        std::ofstream outputFile(tempPath, std::ios::out|std::ios::binary);
        // 检查文件是否成功打开
        if (outputFile.is_open()) {
            // 将缓冲区内容写入文件
            outputFile.write(reinterpret_cast<const char*>(buffer), bufferSize);
            // 关闭文件(析构函数会自动调用,但显式关闭是好习惯)
            outputFile.close();
            OH_LOG_INFO(LOG_APP, "数据写入成功.");
        } else {
            OH_LOG_INFO(LOG_APP, "无法打开文件.");
        }
        char* tempFileUri;
        OH_FileUri_GetUriFromPath(tempPath.c_str(), tempPath.size(), &tempFileUri);
        // 设置文件路径
        OH_AbilityBase_SetWantUri(want, tempFileUri);
    }
    // 启动UIAbility 对文档进行编辑操作
    ret = OH_ContentEmbed_Extension_ContextStartSelfUIAbility(context, want);
    if (ret != CE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "OH_ContentEmbed_Extension_ContextStartSelfUIAbility failed, errCode: %{public}d.", ret);
        return;
    }
}

实现获取OE Extension能力

当OE Extension被启动后,OE客户端会通过OH_ContentEmbed_Proxy_GetCapability获取OE服务端具备的能力,此时会触发OE服务端的NativeOnGetCapability回调,在该回调中OE服务端应用通过给bitmask属性赋值,通知OE客户端自身具备的能力。

static void NativeOnGetCapability(ContentEmbed_ObjectHandle object, uint32_t *bitmask)
{
    if (!bitmask) {
        OH_LOG_ERROR(LOG_APP, "bitmask is null");
        return;
    }
    *bitmask = CE_CAPABILITY_SUPPORT_SNAPSHOT|CE_CAPABILITY_SUPPORT_DO_EDIT;
}

实现查询OE文档编辑状态

当OE Extension被启动后,OE客户端会通过OH_ContentEmbed_Proxy_GetEditStatus获取OE文档编辑状态,此时会触发OE服务端的NativeOnGetEditStatusFunc回调,在该回调中OE服务端应用通知OE客户端文档编辑状态。

static void NativeOnGetEditStatusFunc(ContentEmbed_ObjectHandle object, bool *isEditing, bool *isModified)
{
    if (!isEditing||!isModified) {
        OH_LOG_ERROR(LOG_APP, "param is null");
        return;
    }
    // 若文档未处于编辑态
    *isEditing = false;
    *isModified = false;
}

实现OE文档写流操作

当OE客户端通过已存在文件来嵌入OE对象且OE Extension被启动后,此时会触发OE服务端的NativeOnWriteToDataStream回调,在该回调中OE服务端需往OE文档中写入数据。

static void NativeOnWriteToDataStream(ContentEmbed_ObjectHandle object)
{
    ContentEmbed_ErrorCode ret = CE_ERR_OK;
    ContentEmbed_Document* ceDocument = nullptr;
    // 获取OE文档
    ret = OH_ContentEmbed_Extension_GetContentEmbedDocument(object, &ceDocument);
    if (ret != CE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "OH_ContentEmbed_Extension_GetContentEmbedDocument ret: %{public}d", ret);
        return;
    }
    // 获取Root Storage
    ContentEmbed_Storage *rootStorage = nullptr;
    ret = OH_ContentEmbed_Document_GetRootStorage(ceDocument, &rootStorage);
    if (ret != CE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "OH_ContentEmbed_Document_GetRootStorage ret: %{public}d", ret);
        return;
    }

    ContentEmbed_Stream *destStream;
    char* fileType = "aaa";
    // 获取名字为aaa的流
    ret = OH_ContentEmbed_Storage_GetStream(rootStorage, fileType, &destStream);
    if (ret != CE_ERR_OK) {
        // 获取失败创建名字为aaa的流
        ret = OH_ContentEmbed_Storage_CreateStream(rootStorage, fileType, &destStream);
    }
    // 获取本地文件路径
    char nativeFilePath[MAX_PATH_LENGTH];
    ret = OH_ContentEmbed_Document_GetNativeFilePath(ceDocument, nativeFilePath);
    if (ret != CE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "OH_ContentEmbed_Document_GetNativeFilePath ret: %{public}d", ret);
        return;
    }

    std::string srcStreamPath = std::string(nativeFilePath);
    std::ifstream oriFile(srcStreamPath, std::ios::binary);
    if (!oriFile) {
        OH_LOG_ERROR(LOG_APP, "File not found");
        return;
    }
    oriFile.seekg(0, std::ios::end);
    size_t oriFileSize = oriFile.tellg();
    oriFile.seekg(0, std::ios::beg);
    std::vector<unsigned char> buffer(oriFileSize);
    oriFile.read(reinterpret_cast<char*>(buffer.data()), oriFileSize);
    size_t num = 0;
    // 往OE文档写数据
    ret = OH_ContentEmbed_Stream_Write(destStream, buffer.data(), oriFileSize, &num);
    if (ret != CE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "OH_ContentEmbed_Stream_Write ret: %{public}d", ret);
        return;
    }
    // 刷新OE文档
    ret = OH_ContentEmbed_Document_Flush(ceDocument);
    if (ret != CE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "OH_ContentEmbed_Document_Flush ret: %{public}d", ret);
        return;
    }
}

你可能感兴趣的鸿蒙文章

openharmony 鸿蒙 content-embed-client-guidelines

openharmony 鸿蒙 Readme-CN

openharmony 鸿蒙 content-embed-kit-terminology

openharmony 鸿蒙 client-server-interaction-process

openharmony 鸿蒙 content-embed-faq

openharmony 鸿蒙 content-embed-kit-overview

  • 所属分类: 后端技术
  • 本文标签: 软件 鸿蒙
  • 版权声明: 本文链接 https://seaxiang.com/blog/DzDonUo3