在 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,系统分享面板把渠道选择交给用户,省掉了申请、审核和签名配置,但应用侧也要为“拿不到分享结果”这一设计约束做取舍。 |