查看: 198|回复: 0

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

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在 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,系统分享面板把渠道选择交给用户,省掉了申请、审核和签名配置,但应用侧也要为“拿不到分享结果”这一设计约束做取舍。
回复

使用道具 举报

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

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

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

官方核心成员

关注微信公众号

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

GMT+8, 2026-9-22 14:12 , Processed in 0.020556 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部