查看: 8442|回复: 3

鸿蒙系统分享插件开发:ShareKit与utd适配踩坑

[复制链接]
发表于 2026-9-22 12:00:00 | 显示全部楼层 |阅读模式
在 HarmonyOS 应用里做分享,产品通常要求支持微信、QQ、短信等渠道。原文作者的初始思路是接微信 SDK,但仅申请 AppID 就要等待审核,还要配置签名,完整流程至少一周。后来改用鸿蒙自带的 @kit.ShareKit 调起系统分享面板,由用户自己选择目标应用,不需要接第三方 SDK,开发成本几乎为零。

一、API 调研:systemShare 的四步调用

鸿蒙分享能力位于 @kit.ShareKit 的 systemShare 模块。核心流程可以拆成四步:构造 SharedRecord、创建 SharedData、创建 ShareController、调用 show()。最小代码结构如下:
  1. import { systemShare } from '@kit.ShareKit';
  2. // 构造分享数据记录
  3. const record: systemShare.SharedRecord = {
  4.   utd: 'text/plain', // 统一数据类型
  5.   title: '分享标题', // 可选
  6.   content: '分享内容' // 必填
  7. };
  8. // 创建 SharedData 并添加记录
  9. const shareData = new systemShare.SharedData();
  10. shareData.addRecord(record);
  11. // 创建分享控制器
  12. const controller = new systemShare.ShareController(shareData);
  13. // 监听分享面板关闭
  14. controller.on('dismiss', () => {
  15.   console.log('分享面板已关闭');
  16. });
  17. // 展示分享面板
  18. controller.show();
复制代码

这里 utd(Uniform Type Descriptor)是鸿蒙的统一数据类型,决定系统分享面板会匹配出哪些目标应用。常用的取值包括:text/plain(纯文本,展示微信、QQ、短信、备忘录等)、text/html(网页链接,展示浏览器、微信等)、image/png 与 image/jpeg(图片)、application/pdf(PDF 文件)。utd 不能随意写,必须符合鸿蒙 UTD 规范;一旦写错,分享面板可能一片空白,找不到目标应用。SharedRecord 的 title 可选,但建议填写,因为部分目标应用(如微信)会把它作为分享卡片标题。ShareController 的 show() 不需要手动传 context,会自动获取当前窗口。分享面板关闭后会触发 dismiss 事件,无论用户选择目标应用还是取消,都会进入该回调。

二、插件实现:接口、错误与核心逻辑

原文给出的插件放在 uni_modules/md-share 目录下。先定义统一接口,包括分享结果、分享选项、函数签名与错误码:
  1. // uni_modules/md-share/utssdk/interface.uts
  2. export interface ShareResult {
  3.   errMsg: string
  4. }
  5. export interface ShareOptions {
  6.   title?: string
  7.   content: string
  8.   type?: 'text' | 'link'
  9.   success?: (res: ShareResult) => void
  10.   fail?: (res: any) => void
  11.   complete?: (res: any) => void
  12. }
  13. export type Share = (options: ShareOptions) => void
  14. export type ShareErrorCode = 9200001
  15. export interface ShareFail extends IUniError {
  16.   errCode: ShareErrorCode
  17. }
复制代码

错误处理单独放在 unierror.uts 中,使用 9200001 表示分享失败,并通过 ShareFailImpl 继承 UniError 实现统一错误对象。

核心实现根据 type 选择 utd:text 用 text/plain,link 用 text/html。构造 SharedData、ShareController 并 show(),同时监听 dismiss 回调。若构造或展示过程抛异常,则封装为 9200001 错误返回给 fail/complete:
  1. // uni_modules/md-share/utssdk/app-harmony/index.uts
  2. import { ShareOptions, ShareResult, Share } from '../interface.uts';
  3. import { ShareFailImpl } from '../unierror';
  4. import { systemShare } from '@kit.ShareKit';
  5. import { BusinessError } from '@kit.BasicServicesKit';
  6. export const share: Share = function (options: ShareOptions) {
  7.   try {
  8.     const utdType = options.type === 'link' ? 'text/html' : 'text/plain';
  9.     const record: systemShare.SharedRecord = {
  10.       utd: utdType,
  11.       title: options.title ?? '',
  12.       content: options.content
  13.     };
  14.     const shareData = new systemShare.SharedData();
  15.     shareData.addRecord(record);
  16.     const controller = new systemShare.ShareController(shareData);
  17.     controller.on('dismiss', () => {
  18.       const res: ShareResult = {
  19.         errMsg: 'share:ok'
  20.       };
  21.       options.success?.(res);
  22.       options.complete?.(res);
  23.     });
  24.     controller.show();
  25.   } catch (e) {
  26.     const err = new ShareFailImpl(9200001);
  27.     err.errMsg = '分享失败: ' + (e as Error).message;
  28.     options.fail?.(err);
  29.     options.complete?.(err);
  30.   }
  31. }
复制代码

示例页面位于 pages/share/share.uvue,提供标题输入、内容输入、文本/链接类型选择,并在按钮点击时调用 share:
  1. import { share } from '@/uni_modules/md-share'
  2. const shareTitle = ref('')
  3. const shareContent = ref('快来试试这个功能吧!')
  4. const shareType = ref<'text' | 'link'>('text')
  5. const doShare = () => {
  6.   if (!shareContent.value) {
  7.     uni.showToast({
  8.       title: '请填写分享内容',
  9.       icon: 'none'
  10.     })
  11.     return
  12.   }
  13.   share({
  14.     title: shareTitle.value,
  15.     content: shareContent.value,
  16.     type: shareType.value,
  17.     success: (res) => {
  18.       console.log('分享成功')
  19.       uni.showToast({
  20.         title: '分享成功',
  21.         icon: 'success'
  22.       })
  23.     },
  24.     fail: (err) => {
  25.       console.error('分享失败:', JSON.stringify(err))
  26.       uni.showToast({
  27.         title: err.errMsg || '分享失败',
  28.         icon: 'none'
  29.       })
  30.     }
  31.   })
  32. }
复制代码

三、踩坑记录

坑一:utd 类型写错导致分享面板空白。作者第一次测试时把 utd 写成 'text',结果分享面板弹出后没有任何目标应用。正确做法是使用标准 MIME 格式,例如 text/plain、text/html、image/png、application/pdf。系统需要根据 utd 判断数据类型,写错后匹配不到可接收的应用。

错误写法:
  1. const record: systemShare.SharedRecord = {
  2.   utd: 'text',
  3.   content: '分享内容'
  4. };
复制代码

正确写法:
  1. const record: systemShare.SharedRecord = {
  2.   utd: 'text/plain',
  3.   content: '分享内容'
  4. };
复制代码

坑二:SharedData 必须至少添加一条记录。创建 ShareController 时如果传入空 SharedData,show() 会直接抛异常。必须先 addRecord,再创建控制器并展示。

错误写法:
  1. const shareData = new systemShare.SharedData();
  2. const controller = new systemShare.ShareController(shareData);
  3. controller.show();
复制代码

正确写法:
  1. const shareData = new systemShare.SharedData();
  2. shareData.addRecord(record);
  3. const controller = new systemShare.ShareController(shareData);
  4. controller.show();
复制代码

坑三:无法区分用户是分享成功还是取消。controller.on('dismiss') 只表示分享面板关闭,用户选择微信成功分享或点击返回取消都会触发。鸿蒙没有提供分享结果回调,系统分享面板独立于应用,应用无法感知用户在面板里的操作结果。原文插件因此在 dismiss 时统一按成功处理。如果业务必须知道是否真正分享,只能换思路,例如分享后记录日志,或在用户回到 App 时检查某些状态;对多数场景,dismiss 回调已经够用。

坑四:分享链接时 utd 要用 text/html。作者一开始分享链接用 text/plain,结果分享到微信后只显示纯文本链接,没有标题和摘要。改用 text/html 后,目标应用会识别为网页链接并展示卡片样式。插件通过 type 参数区分:type: 'text' 对应 text/plain,type: 'link' 对应 text/html。
  1. // 分享纯文本
  2. const record = {
  3.   utd: 'text/plain',
  4.   title: '标题',
  5.   content: '这是一段文字'
  6. };
  7. // 分享链接
  8. const record = {
  9.   utd: 'text/html',
  10.   title: '标题',
  11.   content: 'https://example.com'
  12. };
复制代码

总结

这套鸿蒙系统分享插件的核心就是 SharedRecord + SharedData + ShareController 三步,代码放在 uni_modules/md-share 目录,示例页面在 pages/share/share.uvue。真正容易出问题的地方有两个:一是 utd 类型必须选对,否则分享面板可能空白或链接无法呈现卡片;二是要接受 dismiss 回调无法区分成功与取消的限制。相比接入微信 SDK,系统分享面板把渠道选择交给用户,省掉了申请、审核和签名配置,但应用侧也要为“拿不到分享结果”这一设计约束做取舍。
回复

使用道具 举报

发表于 2026-9-22 19:00:00 | 显示全部楼层

Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑

感谢分享,用 ShareKit 调系统分享面板绕开微信 SDK 审核这个思路确实很实用,UTD 和 dismiss 这两个点也很有参考价值。看完有两个地方想请教一下。 一是你说 dismiss 无论用户选择目标应用还是取消都会触发,但插件里是在 dismiss 回调里直接走 success 和 complete,errMsg 写的是 sharek。这样用户点取消也会返回成功,如果业务侧依赖这个结果做后续动作,可能会误判。是不是应该把 success 的语义定义成“已调起分享面板”,或者在文档里明确取消也会走 success? 二是 link 类型用 text/html 时,content 是直接放 URL 就行,还是需要拼成可点击的 HTML?另外 title 可选但代码里用了空字符串兜底,微信卡片收到空 title 会不会反而不好看,是否可以考虑不传 title 字段? 最后你贴的 catch 部分好像截断了,BusinessError 的具体 code 有没有保留到 errMsg 里,方便排查 utd 写错或面板调不起来的问题?整体接口和错误码封装挺清晰的,期待后续图片和 PDF 的适配。
回复 支持 反对

使用道具 举报

发表于 2026-9-22 19:10:00 | 显示全部楼层

Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑

感谢分享,这套基于 ShareKit 的系统分享方案确实比接微信 SDK 省事很多,尤其是绕开 AppID 审核和签名配置这点很实在。UTD 适配那块总结得很到位,text/plain 和 text/html 的映射关系一看就明白,写错会导致分享面板空白这个坑也很有提醒价值。 有个小细节想确认下:你在 dismiss 回调里直接回调 success 和 complete,这样用户点取消也会返回 sharek,如果业务上不区分选择目标应用还是取消,那没问题;但如果要区分,是不是得额外处理一下?另外目前接口只支持 text 和 link,后续如果要支持图片或 PDF,是打算继续扩展 type,还是直接在 options 里传 utd?整体插件结构挺清晰,错误码 9200001 也统一,学习了。
回复 支持 反对

使用道具 举报

发表于 2026-9-22 19:20:00 | 显示全部楼层

Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑

这个分享方案挺实用的,不用接微信 SDK 等审核和签名,成本确实低很多。四步调用整理得很清楚,utd 这块也点到了关键,写错会导致分享面板匹配不到应用。title 建议填写这点也很有用,微信会拿来当卡片标题。 不过有个地方想确认下:dismiss 是选择目标应用和取消都会触发,那插件里统一回调 success 并返回 sharek,调用方可能分不清用户到底分享了还是取消了。如果业务要区分,最好在插件层加个状态或更细的错误码。另外正文最后“分享失败: ”后面像是被截断了,ShareFailImpl 的完整错误设置和 fail、complete 兜底如果补全会更完整。整体封装思路很好,接口和错误码设计也清晰。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-7 03:10 , Processed in 0.036600 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部