鸿蒙系统分享插件开发:ShareKit与utd适配踩坑
在 HarmonyOS 应用里做分享,产品通常要求支持微信、QQ、短信等渠道。原文作者的初始思路是接微信 SDK,但仅申请 AppID 就要等待审核,还要配置签名,完整流程至少一周。后来改用鸿蒙自带的 @kit.ShareKit 调起系统分享面板,由用户自己选择目标应用,不需要接第三方 SDK,开发成本几乎为零。一、API 调研:systemShare 的四步调用
鸿蒙分享能力位于 @kit.ShareKit 的 systemShare 模块。核心流程可以拆成四步:构造 SharedRecord、创建 SharedData、创建 ShareController、调用 show()。最小代码结构如下:
import { systemShare } from '@kit.ShareKit';
// 构造分享数据记录
const record: systemShare.SharedRecord = {
utd: 'text/plain', // 统一数据类型
title: '分享标题', // 可选
content: '分享内容' // 必填
};
// 创建 SharedData 并添加记录
const shareData = new systemShare.SharedData();
shareData.addRecord(record);
// 创建分享控制器
const controller = new systemShare.ShareController(shareData);
// 监听分享面板关闭
controller.on('dismiss', () => {
console.log('分享面板已关闭');
});
// 展示分享面板
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 目录下。先定义统一接口,包括分享结果、分享选项、函数签名与错误码:
// uni_modules/md-share/utssdk/interface.uts
export interface ShareResult {
errMsg: string
}
export interface ShareOptions {
title?: string
content: string
type?: 'text' | 'link'
success?: (res: ShareResult) => void
fail?: (res: any) => void
complete?: (res: any) => void
}
export type Share = (options: ShareOptions) => void
export type ShareErrorCode = 9200001
export interface ShareFail extends IUniError {
errCode: ShareErrorCode
}
错误处理单独放在 unierror.uts 中,使用 9200001 表示分享失败,并通过 ShareFailImpl 继承 UniError 实现统一错误对象。
核心实现根据 type 选择 utd:text 用 text/plain,link 用 text/html。构造 SharedData、ShareController 并 show(),同时监听 dismiss 回调。若构造或展示过程抛异常,则封装为 9200001 错误返回给 fail/complete:
// uni_modules/md-share/utssdk/app-harmony/index.uts
import { ShareOptions, ShareResult, Share } from '../interface.uts';
import { ShareFailImpl } from '../unierror';
import { systemShare } from '@kit.ShareKit';
import { BusinessError } from '@kit.BasicServicesKit';
export const share: Share = function (options: ShareOptions) {
try {
const utdType = options.type === 'link' ? 'text/html' : 'text/plain';
const record: systemShare.SharedRecord = {
utd: utdType,
title: options.title ?? '',
content: options.content
};
const shareData = new systemShare.SharedData();
shareData.addRecord(record);
const controller = new systemShare.ShareController(shareData);
controller.on('dismiss', () => {
const res: ShareResult = {
errMsg: 'share:ok'
};
options.success?.(res);
options.complete?.(res);
});
controller.show();
} catch (e) {
const err = new ShareFailImpl(9200001);
err.errMsg = '分享失败: ' + (e as Error).message;
options.fail?.(err);
options.complete?.(err);
}
}
示例页面位于 pages/share/share.uvue,提供标题输入、内容输入、文本/链接类型选择,并在按钮点击时调用 share:
import { share } from '@/uni_modules/md-share'
const shareTitle = ref('')
const shareContent = ref('快来试试这个功能吧!')
const shareType = ref<'text' | 'link'>('text')
const doShare = () => {
if (!shareContent.value) {
uni.showToast({
title: '请填写分享内容',
icon: 'none'
})
return
}
share({
title: shareTitle.value,
content: shareContent.value,
type: shareType.value,
success: (res) => {
console.log('分享成功')
uni.showToast({
title: '分享成功',
icon: 'success'
})
},
fail: (err) => {
console.error('分享失败:', JSON.stringify(err))
uni.showToast({
title: err.errMsg || '分享失败',
icon: 'none'
})
}
})
}
三、踩坑记录
坑一:utd 类型写错导致分享面板空白。作者第一次测试时把 utd 写成 'text',结果分享面板弹出后没有任何目标应用。正确做法是使用标准 MIME 格式,例如 text/plain、text/html、image/png、application/pdf。系统需要根据 utd 判断数据类型,写错后匹配不到可接收的应用。
错误写法:
const record: systemShare.SharedRecord = {
utd: 'text',
content: '分享内容'
};
正确写法:
const record: systemShare.SharedRecord = {
utd: 'text/plain',
content: '分享内容'
};
坑二:SharedData 必须至少添加一条记录。创建 ShareController 时如果传入空 SharedData,show() 会直接抛异常。必须先 addRecord,再创建控制器并展示。
错误写法:
const shareData = new systemShare.SharedData();
const controller = new systemShare.ShareController(shareData);
controller.show();
正确写法:
const shareData = new systemShare.SharedData();
shareData.addRecord(record);
const controller = new systemShare.ShareController(shareData);
controller.show();
坑三:无法区分用户是分享成功还是取消。controller.on('dismiss') 只表示分享面板关闭,用户选择微信成功分享或点击返回取消都会触发。鸿蒙没有提供分享结果回调,系统分享面板独立于应用,应用无法感知用户在面板里的操作结果。原文插件因此在 dismiss 时统一按成功处理。如果业务必须知道是否真正分享,只能换思路,例如分享后记录日志,或在用户回到 App 时检查某些状态;对多数场景,dismiss 回调已经够用。
坑四:分享链接时 utd 要用 text/html。作者一开始分享链接用 text/plain,结果分享到微信后只显示纯文本链接,没有标题和摘要。改用 text/html 后,目标应用会识别为网页链接并展示卡片样式。插件通过 type 参数区分:type: 'text' 对应 text/plain,type: 'link' 对应 text/html。
// 分享纯文本
const record = {
utd: 'text/plain',
title: '标题',
content: '这是一段文字'
};
// 分享链接
const record = {
utd: 'text/html',
title: '标题',
content: 'https://example.com'
};
总结
这套鸿蒙系统分享插件的核心就是 SharedRecord + SharedData + ShareController 三步,代码放在 uni_modules/md-share 目录,示例页面在 pages/share/share.uvue。真正容易出问题的地方有两个:一是 utd 类型必须选对,否则分享面板可能空白或链接无法呈现卡片;二是要接受 dismiss 回调无法区分成功与取消的限制。相比接入微信 SDK,系统分享面板把渠道选择交给用户,省掉了申请、审核和签名配置,但应用侧也要为“拿不到分享结果”这一设计约束做取舍。
Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑
感谢分享,用 ShareKit 调系统分享面板绕开微信 SDK 审核这个思路确实很实用,UTD 和 dismiss 这两个点也很有参考价值。看完有两个地方想请教一下。 一是你说 dismiss 无论用户选择目标应用还是取消都会触发,但插件里是在 dismiss 回调里直接走 success 和 complete,errMsg 写的是 share:ok。这样用户点取消也会返回成功,如果业务侧依赖这个结果做后续动作,可能会误判。是不是应该把 success 的语义定义成“已调起分享面板”,或者在文档里明确取消也会走 success? 二是 link 类型用 text/html 时,content 是直接放 URL 就行,还是需要拼成可点击的 HTML?另外 title 可选但代码里用了空字符串兜底,微信卡片收到空 title 会不会反而不好看,是否可以考虑不传 title 字段? 最后你贴的 catch 部分好像截断了,BusinessError 的具体 code 有没有保留到 errMsg 里,方便排查 utd 写错或面板调不起来的问题?整体接口和错误码封装挺清晰的,期待后续图片和 PDF 的适配。Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑
感谢分享,这套基于 ShareKit 的系统分享方案确实比接微信 SDK 省事很多,尤其是绕开 AppID 审核和签名配置这点很实在。UTD 适配那块总结得很到位,text/plain 和 text/html 的映射关系一看就明白,写错会导致分享面板空白这个坑也很有提醒价值。 有个小细节想确认下:你在 dismiss 回调里直接回调 success 和 complete,这样用户点取消也会返回 share:ok,如果业务上不区分选择目标应用还是取消,那没问题;但如果要区分,是不是得额外处理一下?另外目前接口只支持 text 和 link,后续如果要支持图片或 PDF,是打算继续扩展 type,还是直接在 options 里传 utd?整体插件结构挺清晰,错误码 9200001 也统一,学习了。Re: 鸿蒙系统分享插件开发:ShareKit与utd适配踩坑
这个分享方案挺实用的,不用接微信 SDK 等审核和签名,成本确实低很多。四步调用整理得很清楚,utd 这块也点到了关键,写错会导致分享面板匹配不到应用。title 建议填写这点也很有用,微信会拿来当卡片标题。 不过有个地方想确认下:dismiss 是选择目标应用和取消都会触发,那插件里统一回调 success 并返回 share:ok,调用方可能分不清用户到底分享了还是取消了。如果业务要区分,最好在插件层加个状态或更细的错误码。另外正文最后“分享失败: ”后面像是被截断了,ShareFailImpl 的完整错误设置和 fail、complete 兜底如果补全会更完整。整体封装思路很好,接口和错误码设计也清晰。
页:
[1]