把内容分享出去容易,把别人分享过来的内容正确接住才是真麻烦。

做笔记应用的时候,最开始只做了"分享出去":写完笔记点分享,把文本和图片传给其他应用。这个功能很简单,调用 Share Kit 的 API 就完事了。
后来用户提需求:能不能在相册里选一张图,直接分享到我们的笔记应用里?这时候才发现,"接收分享"比"分享出去"麻烦多了——要配置接收类型、要解析跨应用传过来的数据、要处理各种异常情况、还要管理分享页面的生命周期。
这篇就沿着"一张图片从相册跑到笔记应用"这条链路,把整个分享接收流程拆清楚。
一、一张图片的跨应用旅程
先把整个链路理一遍:
相册应用
↓ 调用系统分享
系统分享面板
↓ 用户选择笔记应用
笔记应用 ShareExtensionAbility
↓ 接收 SharedData
解析数据 → 保存图片 → 创建笔记
整个过程可以想成"寄快递":
- 发送方把内容打包成 SharedData;
- 系统分享面板是快递公司,负责路由;
- 接收方的 ShareExtensionAbility 是收件人,负责拆包、检查、处理。
二、ShareExtensionAbility 是干什么的
ShareExtensionAbility 基于 UIExtensionAbility,它的作用就是让你的应用出现在系统分享面板里,并且能处理其他应用传过来的分享内容。
| 角色 | 职责 |
|---|---|
| 发送方应用 | 把内容打包成 SharedData,调用系统分享 |
| 系统分享面板 | 展示可接收的应用列表,路由分享内容 |
| ShareExtensionAbility(接收方) | 接收 SharedData,解析数据,处理业务 |
很多人只实现了"分享出去",没实现"接收分享"。结果就是你的应用在分享列表里根本看不到,因为你根本没注册 ShareExtensionAbility。
三、UTD 类型为什么这么重要
分享的数据不是随便传的,要有类型标识。这个类型就是 UTD(Uniform Type Description)。
| UTD 类型 | 对应内容 |
|---|---|
| text/plain | 纯文本 |
| image/jpeg | JPEG 图片 |
| image/png | PNG 图片 |
| file/* | 通用文件 |
你要接收什么类型的数据,就要在 module.json5 里配置对应的 UTD 类型。配置错了,系统分享面板里就不会出现你的应用,或者出现了但收不到对应类型的数据。
这段代码解决什么问题: 配置应用可接收的分享类型。
文件: entry/src/main/module.json5
用途: 注册 ShareExtensionAbility 接收类型
接入位置: ExtensionAbility 配置
"extensionAbilities": [
{
"name": "ShareReceiverAbility",
"type": "shareReceiver",
"metadata": {
"utd": ["text/plain", "image/jpeg", "image/png"],
"maxFileSupported": 20
}
}
]
这里配置了可以接收文本、JPEG、PNG 三种类型,最多支持 20 个文件。maxFileSupported 很重要——超过这个数量,系统就不会把你的应用显示在分享列表里了。
四、收到数据之后怎么解析
用户选了你的应用之后,ShareExtensionAbility 的 onSessionCreate 会被调用,里面带着传过来的 SharedData。
这段代码解决什么问题: 解析接收到的分享数据。
文件: ShareReceiverAbility.ets
用途: 接收并处理分享内容
接入位置: onSessionCreate 生命周期
import ShareExtensionAbility from '@ohos.app.ability.ShareExtensionAbility';
import Want from '@ohos.app.ability.Want';
export default class ShareReceiver extends ShareExtensionAbility {
onSessionCreate(want: Want, session) {
const sharedData = want?.parameters?.sharedData;
const records = sharedData?.records || [];
for (const record of records) {
if (record.utd === 'image/jpeg' || record.utd === 'image/png') {
this.saveImage(record.uri);
} else if (record.utd === 'text/plain') {
this.saveText(record.content);
}
}
this.finishShare();
}
}
这里要注意:不同类型的 record 结构不一样。图片类型的 record 里是文件 URI,文本类型的 record 里是 content 字段。不能所有 record 都按同一种方式解析。
五、URI 为什么不能直接当路径用
这是最容易踩的坑。接收过来的图片 URI,不是应用沙箱里的文件路径,是系统媒体库的资源标识。
| 错误做法 | 正确做法 |
|---|---|
| 把 URI 直接当文件路径打开 | 通过系统 Kit 的接口读取资源 |
| 拼接字符串猜真实路径 | 用文件管理接口按 URI 读取 |
| 长期缓存 URI 直接用 | 读取后复制到应用沙箱再使用 |
跨应用传过来的 URI 是有访问权限的,不是永久有效的。处理完之后最好把文件复制到自己的应用沙箱里,再做后续处理。

六、Session 为什么必须正确结束
处理完分享内容之后,必须调用 finishShare 或者 session.terminate() 结束这次分享会话。
如果不结束会怎么样?系统分享面板一直挂着,用户操作完了页面不消失,体验很差。更严重的是,Session 不结束,资源一直不释放,占着内存。
还有异常情况:如果处理数据出错了,不能只写个日志就不管了。要给用户一个提示,然后正常结束 Session。不能让系统分享面板卡在那里。
七、几个容易踩的坑
第一个坑:只做"分享出去",没做"接收分享"。结果应用在分享列表里都不出现。
第二个坑:UTD 类型配置错误。配置的类型和实际要接收的类型对不上,要么收不到数据,要么系统不显示。
第三个坑:图片数量超过 maxFileSupported。一次分享了 30 张图,但配置的最多支持 20 张,那你的应用直接从分享列表里消失了。
第四个坑:把 URI 当普通文件路径。跨应用传过来的 URI 不是沙箱路径,直接 open 会失败。
第五个坑:所有数据类型都按同一种方式解析。文本和图片的 record 结构不一样,混着解析就会出错。

这次做分享接收功能最大的体会是:分享出去只是把数据打包扔出去,接收分享才是真正的工程活。要配置类型、要解析数据、要处理 URI、要管理 Session、要处理异常。看起来就是"点一下分享按钮"的事,背后有一整套跨应用数据流转的机制在跑。

1120

被折叠的 条评论
为什么被折叠?



