COOL团队
COOL团队
  • 发布:2025-10-31 00:01
  • 更新:2025-10-31 00:01
  • 阅读:8

【鸿蒙征文】折腾鸿蒙分享功能的那些事儿

分类:鸿蒙Next

事情是这样的

前两天在家撸代码,突然想给我的小破app加个分享功能。本来想偷个懒,直接用现成的插件算了。结果一看,好家伙,都要收费。我寻思着这玩意儿能有多难?不就是调个系统API嘛,自己搞一个呗。

没想到这一入坑,还真挺有意思。鸟蒙的API设计还挺人性化的,虽然踩了不少坑,但学到的东西也不少。

文件咋放的

也没搞得很复杂,就几个文件:

cool-share/  
├── utssdk/  
    ├── interface.uts      # 给别人用的接口  
    └── app-harmony/       # 鸿蒙专用代码  
        ├── index.uts      # 入口文件  
        └── share.ets      # 干活的代码

先定个规矩

接口嘛,就是告诉别人怎么调用我这个东西:

export type ShareWithSystemOptions = {  
    type: string; // 分享啥类型的玩意儿  
    title?: string; // 起个标题  
    summary?: string; // 写点描述  
    href?: string; // 链接或者文件在哪儿  
    imageUrl?: string; // 图片视频的地址  
    success?: () => void; // 成功了干啥  
    fail?: (error: string) => void; // 失败了咋办  
};

开始干活了

这块儿是重点,也是我掉坑最多的地方。

先把家伙事儿准备好

import { systemShare } from "@kit.ShareKit";  
import { uniformTypeDescriptor as utd } from "@kit.ArkData";  
import { common } from "@kit.AbilityKit";  
import { fileUri } from "@kit.CoreFileKit";  
import { UTSHarmony } from "@dcloudio/uni-app-x-runtime";

这些都是鸿蒙给咱准备的工具,分别管分享、识别文件类型、获取应用信息、处理文件路径这些活儿。

分享类型定义

enum ShareType {  
    TEXT = "text", // 纯文本  
    IMAGE = "image", // 图片  
    VIDEO = "video", // 视频  
    AUDIO = "audio", // 音频  
    FILE = "file", // 文件  
    LINK = "link" // 链接  
}

分享图片咋整

图片分享最常用,咱先搞这个:

function createImageShareData(  
    imageUrl: string,  
    title: string,  
    summary: string  
): systemShare.SharedData | null {  
    if (imageUrl === "") {  
        return null;  
    }  

    // 这里要注意,需要先获取正确的文件路径  
    const filePath = UTSHarmony.getResourcePath(imageUrl);  

    // 然后获取文件的数据类型标识符  
    const utdTypeId = getUtdTypeByPath(filePath, utd.UniformDataType.IMAGE);  

    // 最后创建分享数据对象  
    return new systemShare.SharedData({  
        utd: utdTypeId, // 数据类型  
        uri: fileUri.getUriFromPath(filePath), // 文件URI  
        title: title, // 标题  
        description: summary // 描述  
    });  
}

怎么用这玩意儿

在你的页面里这么写就行:

import { shareWithSystem } from "@/uni_modules/cool-share";  

// 分享个图片  
shareWithSystem({  
    type: "image",  
    title: "我拍的照片",  
    summary: "今天拍的,还不错吧",  
    imageUrl: "https://cool-js.com/logo.png",  
    success: () => {  
        console.log("success");  
    },  
    fail: (error) => {  
        console.log(error);  
    }  
});  

// 分享点文字  
shareWithSystem({  
    type: "text",  
    title: "今日心情",  
    summary: "今天天气不错,心情美美哒~",  
    success: () => {  
        console.log("success");  
    }  
});  

// 分享个链接  
shareWithSystem({  
    type: "link",  
    title: "发现个好网站",  
    summary: "这网站挺有意思的,你们看看",  
    href: "https://cool-js.com/",  
    success: () => {  
        console.log("success");  
    }  
});

UTD是个啥东西

鸿蒙用UTD(统一数据类型标识符)来识别文件类型。简单说就是告诉系统:"嘿,这是个图片!"或者"这是个视频!"

function getUtdTypeByPath(filePath: string, defaultType: string): string {  
    const ext = filePath?.split(".")?.pop()?.toLowerCase() ?? "";  
    if (ext === "") {  
        return defaultType;  
    }  
    return utd.getUniformDataTypeByFilenameExtension("." + ext, defaultType);  
}

这个函数就是看文件后缀名,.jpg就知道是图片,.mp4就知道是视频,就这么简单。

文件分享稍微麻烦点

文件分享比较复杂,得看是啥类型的文件:

function createFileShareData(  
    filePath: string,  
    title: string,  
    summary: string  
): systemShare.SharedData | null {  
    if (filePath === "") {  
        return null;  
    }  

    const resourcePath = UTSHarmony.getResourcePath(filePath);  
    const ext = resourcePath?.split(".")?.pop()?.toLowerCase() ?? "";  

    // 根据文件扩展名确定数据类型  
    let utdType = utd.UniformDataType.FILE;  

    switch (ext) {  
        case "zip":  
        case "rar":  
        case "7z":  
            utdType = utd.UniformDataType.ARCHIVE; // 压缩包  
            break;  
        case "pdf":  
            utdType = utd.UniformDataType.PDF; // PDF文档  
            break;  
        case "doc":  
        case "docx":  
            utdType = utd.UniformDataType.WORD_DOC; // Word文档  
            break;  
        case "xls":  
        case "xlsx":  
            utdType = utd.UniformDataType.EXCEL; // Excel表格  
            break;  
        default:  
            utdType = utd.UniformDataType.FILE; // 普通文件  
            break;  
    }  

    const utdTypeId = utd.getUniformDataTypeByFilenameExtension("." + ext, utdType);  

    return new systemShare.SharedData({  
        utd: utdTypeId,  
        uri: fileUri.getUriFromPath(resourcePath),  
        title: title,  
        description: summary  
    });  
}

最后一步,弹出分享框

数据都准备好了,现在可以叫出系统的分享面板了:

export function share(  
    type: string,  
    title: string,  
    summary: string,  
    href: string,  
    imageUrl: string,  
    success: () => void,  
    fail: (error: string) => void  
): void {  
    // 先获取当前应用的上下文,这个是必须的  
    const uiContext: UIContext = UTSHarmony.getCurrentWindow()?.getUIContext();  
    const context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;  

    // 根据不同类型创建对应的分享数据  
    let shareData: systemShare.SharedData | null = null;  
    let errorMsg = "";  

    switch (type) {  
        case ShareType.IMAGE:  
            shareData = createImageShareData(imageUrl, title, summary);  
            errorMsg = "图片路径不能为空";  
            break;  
        case ShareType.VIDEO:  
            shareData = createVideoShareData(imageUrl, title, summary);  
            errorMsg = "视频路径不能为空";  
            break;  
        case ShareType.LINK:  
            shareData = createLinkShareData(href, title, summary);  
            break;  
        default:  
            // 默认当文本处理  
            shareData = createTextShareData(title, summary);  
            break;  
    }  

    // 检查数据是否有效  
    if (shareData === null) {  
        fail(errorMsg);  
        return;  
    }  

    // 创建分享控制器  
    const controller: systemShare.ShareController = new systemShare.ShareController(shareData);  

    // 显示分享面板  
    controller  
        .show(context, {  
            selectionMode: systemShare.SelectionMode.SINGLE, // 单选模式  
            previewMode: systemShare.SharePreviewMode.DEFAULT // 默认预览  
        })  
        .then(() => {  
            success(); // 分享成功  
        })  
        .catch((error: BusinessError) => {  
            fail(error?.message ?? "分享失败"); // 分享失败  
        });  
}

ETS开发小技巧

在写这个插件的过程中,我总结了一些ETS开发的小技巧:

1. 空值安全处理

ETS对空值检查很严格,要养成使用??操作符的习惯:

// 好习惯:使用空值合并操作符  
const ext = filePath?.split(".")?.pop()?.toLowerCase() ?? "";  

// 而不是这样(可能报错)  
const ext = filePath.split(".").pop().toLowerCase();

2. 类型断言要谨慎

尽量避免使用as进行强制类型转换,多用类型检查:

// 推荐的写法  
if (context instanceof common.UIAbilityContext) {  
    // 安全地使用context  
}  

// 而不是直接断言  
const context = uiContext.getHostContext() as common.UIAbilityContext;

3. 错误处理要完整

鸿蒙的异步操作都是Promise,记得处理catch:

controller  
    .show(context, options)  
    .then(() => {  
        success();  
    })  
    .catch((error: BusinessError) => {  
        // 一定要处理错误情况  
        const errorMessage = error?.message ?? "未知错误";  
        fail(errorMessage);  
    });

4. 文件路径处理

鸿蒙对文件路径很敏感,一定要用正确的API获取路径:

// 正确的做法  
const filePath = UTSHarmony.getResourcePath(imageUrl);  
const uri = fileUri.getUriFromPath(filePath);  

// 而不是直接拼接路径

我踩过的那些坑

1. 分享框死活出不来

刚开始的时候,代码写好了,点击分享按钮啥反应都没有。搞了半天才发现是context获取有问题:

// 错误的做法 - 可能获取不到context  
const context = UTSHarmony.getUniActivity();  

// 正确的做法  
const uiContext = UTSHarmony.getCurrentWindow()?.getUIContext();  
const context = uiContext.getHostContext() as common.UIAbilityContext;

2. 文件跟我玩捉迷藏

本地文件分享老是失败,我还以为是代码逻辑有问题。结果折腾了一晚上才发现,是文件路径搞错了:

// 错误:直接使用相对路径  
imageUrl: "./static/image.jpg";  

// 正确:使用绝对路径或者让UTSHarmony处理  
imageUrl: "/static/image.jpg";  
const realPath = UTSHarmony.getResourcePath(imageUrl);

3. 文件类型识别翻车了

有时候文件类型识别不对,分享到微信QQ就会出现奇怪的问题:

// 保险的做法:先判断扩展名,再设置UTD类型  
let utdType = utd.UniformDataType.FILE; // 默认类型  
switch (ext) {  
    case "jpg":  
    case "jpeg":  
    case "png":  
        utdType = utd.UniformDataType.IMAGE;  
        break;  
    // 其他类型...  
}

4. 异步操作坑死人

Promise的错误处理千万别偷懒,不然出了问题你都不知道哪里错了,我就吃过这个亏:

controller  
    .show(context, options)  
    .then(() => {  
        console.log("分享成功"); // 调试信息很重要  
        success();  
    })  
    .catch((error: BusinessError) => {  
        console.error("分享失败:", error); // 打印错误信息  
        fail(error?.message ?? "分享失败");  
    });

5. 权限这茬儿

有时候会遇到权限不够的情况,特别是读取文件的时候。记得检查app的权限配置,不然用户点了分享啥反应都没有。

一些常见问题

Q: 分享面板怎么是空白的?

A: 检查这几个地方:

  1. context获取对了没
  2. ShareData有没有创建成功(别返回null)
  3. 文件路径对不对
  4. UTD类型匹配不

Q: 为啥有些app收不到我分享的东西?

A: 估计是UTD类型不匹配,或者那个app不认这种数据格式。试试用通用一点的类型,比如utd.UniformDataType.FILE

Q: 网络上的图片能直接分享吗?

A: 不行,得先下载到本地,然后分享本地文件。

Q: 分享大文件会卡吗?

A: 会的,特别是视频文件。建议加个转圈圈的loading,或者提前压缩一下。

Q: 能知道用户选了哪个app分享吗?

A: 目前鸿蒙的API不支持,只能知道用户是分享成功了还是取消了。

Q: 能自己做个分享面板吗?

A: 不行,只能用系统提供的。不过可以通过selectionModepreviewMode参数稍微调整一下样式。

测试这块儿

真机测试的时候发现了不少问题,建议你们也多试试:

  • 各种文件类型:图片、视频、文档啥的都试试
  • 文件大小:小图片秒传,大视频可能要等一会儿
  • 不同app:微信、QQ、邮箱的接收效果都不一样
  • 网络状况:网不好的时候网络图片可能加载不出来

调试的时候多打印,鸿蒙的报错信息有时候说得不够清楚。

总结

折腾了好几天,总算把这个分享功能搞定了。整体感觉鸿蒙的API还挺人性化的,比我想象中好用多了。

几个心得:

  1. 多翻文档:鸿蒙官方文档写得还可以,遇到问题先去翻翻
  2. 类型别偷懒:ETS的类型检查确实严格,但写出来的代码更稳定
  3. 错误要处理好:异步操作的错误处理千万别省,不然出了bug找都找不到
  4. 真机多测试:模拟器和真机表现可能不一样,都试试保险点

现在这个小插件基本能满足日常需要了。如果你也在搞类似的东西,希望我这些踩坑经验能帮到你。有问题咱们可以一起交流。

所有代码都在uni_modules/cool-share目录里,有兴趣的朋友可以看看。

顺便安利个东西

我们团队还做了个 Cool Unix 组件库,也是基于 Uni-App X 的,完全免费开源。里面有 Tailwind CSS、多主题、国际化这些实用功能,还有挺多现成的组件和页面模板。

最有意思的是,这个组件库配合AI生成鸿蒙页面效果特别好,以后还准备加上后端接口自动生成,真正做到一句话就能搞出个app。如果你也想快速开发鸿蒙应用,可以试试看。

最后感谢一下

感谢 uni-app x 团队做出这么棒的跨平台框架,让我们这些普通开发者也能轻松搞定多端开发。特别是UTS语言和鸿蒙支持,真的降低了不少门槛。

0 关注 分享

要回复文章请先登录注册