跳到主要内容鸿蒙 Share Kit 目标应用开发指南:UIAbility 与扩展能力接入实战 | 极客日志TypeScript大前端
鸿蒙 Share Kit 目标应用开发指南:UIAbility 与扩展能力接入实战
鸿蒙 Share Kit 目标应用接入方案详解。涵盖 UIAbility 独立页面处理与 ShareExtensionAbility 嵌入式弹窗两种核心模式,对比适用场景与配置差异。重点解析 module.json5 能力声明、系统分享数据解析、联系人推荐区共享及设计规范。提供完整 ArkTS 代码示例,解决权限不足、URI 访问等常见问题,助力构建流畅的跨应用分享体验。
古灵精怪0 浏览 完整的分享链路中,目标应用的接入同样关键——它需要正确接收分享数据、提供友好的处理界面,甚至支持联系人推荐等进阶功能。本文将聚焦目标应用的全流程开发,从数据接收、分享详情页实现,到联系人共享和设计规范。
一、目标应用核心接入方式:UIAbility vs ShareExtensionAbility
目标应用接收分享数据主要有两种方式,分别适配不同的用户体验场景,我们需根据业务需求选择:
1.1 两种接入方式对比
| 接入方式 | 核心特点 | 适用场景 | 跳转体验 |
|---|
| UIAbility 接入 | 通过独立的 UIAbility 接收数据,打开完整应用页面 | 需对分享内容进行复杂处理(如编辑、保存到特定目录)的应用(如相册、文档编辑器) | 跳转至应用独立页面,处理完成后需手动返回 |
| ShareExtensionAbility 接入 | 基于 UIExtensionAbility,提供嵌入式分享详情页 | 快速完成分享操作(如发送给好友、发布动态)的应用(如社交、短视频 App) | 弹窗式嵌入分享面板,操作后可直接关闭或返回面板 |
1.2 关键前提:配置 module.json5 声明能力
无论哪种接入方式,目标应用都需在 module.json5 中注册分享能力,让系统识别支持的分享类型。核心配置规则:
actions 必须设为 ohos.want.action.sendData(系统分享的标准动作)。
uris 需穷举支持的 UTD 类型(如文本、图片),并指定单次可接收的最大文件数。
- 应用图标和名称通过
icon 和 label 配置,将显示在分享面板的'分享方式区'。
二、方式一:UIAbility 接入(独立页面处理分享)
UIAbility 是 HarmonyOS 应用的基础页面容器,适合需要完整页面交互的分享场景(如将图片保存到相册、编辑分享的文本)。
2.1 完整开发步骤
步骤 1:配置 module.json5 注册能力
{
"module": {
"abilities": [
{
"name": "ShareReceiveUIAbility",
"srcEntry": "./ets/abilities/ShareReceiveUIAbility.ets",
"exported":
true
,
"icon"
:
"$media:share_receive_icon"
,
"label"
:
"$string:share_receive_label"
,
"skills"
:
[
{
"actions"
:
[
"ohos.want.action.sendData"
]
,
"uris"
:
[
{
"scheme"
:
"file"
,
"utd"
:
"general.text"
,
"maxFileSupported"
:
1
}
,
{
"scheme"
:
"file"
,
"utd"
:
"general.image"
,
"maxFileSupported"
:
9
}
]
}
]
}
]
}
}
步骤 2:实现 UIAbility 接收并处理分享数据
在 UIAbility 中通过 systemShare.getSharedData() 解析分享数据,并判断应用是否被系统分享拉起:
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { systemShare } from '@kit.ShareKit';
import { BusinessError } from '@kit.BasicServicesKit';
import promptAction from '@ohos.promptAction';
export default class ShareReceiveUIAbility extends UIAbility {
private sharedData: systemShare.SharedData | null = null;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 关键:判断应用是否被系统分享拉起
if (launchParam.launchReasonMessage === 'ReasonMessage_SystemShare') {
console.info('应用被系统分享拉起,开始处理分享数据');
this.parseSharedData(want);
} else {
console.info('应用正常启动,无分享数据');
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReasonMessage === 'ReasonMessage_SystemShare') {
console.info('应用已启动,接收新的分享数据');
this.parseSharedData(want);
}
}
private parseSharedData(want: Want) {
systemShare.getSharedData(want).then((data: systemShare.SharedData) => {
this.sharedData = data;
const records = data.getRecords();
records.forEach((record, index) => {
console.info(`第${index + 1}条分享数据:UTD=${record.utd}, URI=${record.uri}`);
});
this.loadShareHandlePage();
}).catch((error: BusinessError) => {
console.error(`解析失败:Code=${error.code}`);
promptAction.showToast({ message: '接收分享失败' });
this.terminateSelf();
});
}
private loadShareHandlePage() {
this.onWindowStageCreate = (windowStage: window.WindowStage) => {
windowStage.loadContent('pages/ShareHandlePage', (error) => {
if (error.code) {
console.error(`页面加载失败:${error.message}`);
this.terminateSelf();
return;
}
const pageRouter = windowStage.getMainWindow().getUIContext().router;
pageRouter.pushUrl({ url: 'pages/ShareHandlePage', params: { sharedData: this.sharedData } });
});
};
}
}
步骤 3:实现分享处理页面(ArkUI)
创建 ShareHandlePage.ets,提供图片预览、保存等交互功能:
import { Image, Button, Column, Text, ScrollView } from '@kit.ArkUI';
import { systemShare } from '@kit.ShareKit';
import { fileIo } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import promptAction from '@ohos.promptAction';
@Component
struct ShareHandlePage {
@State sharedData: systemShare.SharedData | null = null;
private context = getContext(this) as common.UIAbilityContext;
build() {
Column({ space: 20 }) {
Text('收到分享内容').fontSize(20).fontWeight(FontWeight.Bold);
if (!this.sharedData) {
Text('暂无有效分享数据').color('#666');
return;
}
ScrollView({ scrollDirection: Axis.Vertical }) {
Column({ space: 15 }) {
this.sharedData.getRecords().forEach((record) => {
if (record.utd.includes('image')) {
Image(record.uri).width(300).height(200).objectFit(ImageFit.Contain);
} else if (record.utd.includes('text')) {
Text(`文本内容:${record.content}`).width('100%').padding(10).backgroundColor('#F5F5F5');
}
});
}.height(300).width('100%');
}
Button('保存到本地').width(200).height(45).backgroundColor('#007AFF').onClick(() => this.saveSharedData());
}.padding(20).width('100%').justifyContent('center');
}
private async saveSharedData() {
if (!this.sharedData) return;
try {
const records = this.sharedData.getRecords();
for (const record of records) {
if (record.utd.includes('image')) {
const filePath = record.uri.replace('file://', '');
const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
const fileData = fileIo.readSync(file.fd);
fileIo.closeSync(file);
const saveDir = `${this.context.filesDir}/share_received`;
if (!fileIo.accessSync(saveDir)) {
fileIo.mkdirSync(saveDir, { recursive: true });
}
const savePath = `${saveDir}/${Date.now()}.jpg`;
const saveFile = fileIo.openSync(savePath, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE);
fileIo.writeSync(saveFile.fd, fileData);
fileIo.closeSync(saveFile);
}
}
promptAction.showToast({ message: '保存成功' });
this.context.terminateSelf();
} catch (error) {
console.error(`保存失败:${(error as Error).message}`);
promptAction.showToast({ message: '保存失败' });
}
}
}
2.2 关键说明
onNewWant 方法:当应用已在后台运行,再次被系统分享拉起时,不会触发 onCreate,而是触发 onNewWant,需在此方法中重新解析分享数据。
- UTD 通配规则:使用
general.image 可支持所有图片类型(如 PNG、JPEG),无需逐个声明;若需精准限制类型,可指定 general.png、general.jpeg。
- 权限处理:若需将分享数据保存到公共目录(如系统图库),需额外申请
ohos.permission.WRITE_MEDIA 权限,沙箱内保存无需额外权限。
三、方式二:ShareExtensionAbility 接入(嵌入式分享详情页)
ShareExtensionAbility 是专门用于处理分享的扩展能力,提供嵌入式弹窗界面(分享详情页),用户无需离开分享面板即可完成操作,体验更流畅,适合社交类应用(如发送给好友、发布动态)。
3.1 完整开发步骤
步骤 1:配置 module.json5 注册扩展能力
{
"module": {
"extensionAbilities": [
{
"name": "ShareExtensionAbility",
"srcEntry": "./ets/abilities/ShareExtensionAbility.ets",
"type": "share",
"exported": true,
"icon": "$media:share_ext_icon",
"label": "$string:share_ext_label",
"skills": [
{
"actions": ["ohos.want.action.sendData"],
"uris": [
{ "scheme": "file", "utd": "general.text", "maxFileSupported": 1 },
{ "scheme": "file", "utd": "general.image", "maxFileSupported": 3 }
]
}
]
}
]
}
}
步骤 2:实现 ShareExtensionAbility 与分享详情页
import { ShareExtensionAbility, UIExtensionContentSession, Want } from '@kit.AbilityKit';
import { systemShare } from '@kit.ShareKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class ShareExtensionAbility extends ShareExtensionAbility {
private session: UIExtensionContentSession | null = null;
private sharedData: systemShare.SharedData | null = null;
private contactInfo: systemShare.ContactInfo | null = null;
onSessionCreate(want: Want, session: UIExtensionContentSession) {
this.session = session;
if (this.launchParam.launchReasonMessage === 'ReasonMessage_SystemShare') {
this.parseShareData(want);
} else {
this.closePanelWithError('非系统分享拉起');
}
}
private async parseShareData(want: Want) {
try {
const [data, contact] = await Promise.all([
systemShare.getSharedData(want),
systemShare.getContactInfo(want).catch(() => null)
]);
this.sharedData = data;
this.contactInfo = contact;
this.session?.loadContent('pages/ShareExtensionPage', { sharedData: data, contactInfo: contact });
} catch (error) {
const err = error as BusinessError;
console.error(`解析失败:Code=${err.code}`);
this.closePanelWithError('接收分享数据失败');
}
}
private closePanelWithError(message: string) {
promptAction.showToast({ message });
this.session?.terminateSelfWithResult({ resultCode: systemShare.ShareAbilityResultCode.ERROR });
}
public closePanel(resultCode: systemShare.ShareAbilityResultCode) {
this.session?.terminateSelfWithResult({ resultCode });
}
}
步骤 3:实现分享详情页 UI(嵌入式弹窗)
创建 ShareExtensionPage.ets,提供快速发送、选择联系人等功能:
import { Column, Text, Button, Image, Flex, List, ListItem } from '@kit.ArkUI';
import { systemShare } from '@kit.ShareKit';
import promptAction from '@ohos.promptAction';
const mockContacts = [
{ id: '1', name: '小明', avatar: '$media:avatar1' },
{ id: '2', name: '小红', avatar: '$media:avatar2' },
{ id: '3', name: '小刚', avatar: '$media:avatar3' }
];
@Component
struct ShareExtensionPage {
@Link sharedData: systemShare.SharedData;
@Link contactInfo: systemShare.ContactInfo | null;
private extension = getContext(this) as unknown as ShareExtensionAbility;
build() {
Column({ space: 15 }) {
Flex({ justifyContent: 'space-between', alignItems: 'center' }) {
Text('发送给好友').fontSize(18).fontWeight(FontWeight.Bold);
Button('取消').textStyle({ fontSize: 14, color: '#666' }).backgroundColor('transparent').onClick(() => {
this.extension.closePanel(systemShare.ShareAbilityResultCode.BACK);
});
}.width('100%');
Column({ space: 8 }) {
Text('分享内容:').fontSize(14).color('#666');
if (this.sharedData.getRecords()[0].utd.includes('image')) {
Image(this.sharedData.getRecords()[0].uri).width(80).height(80).objectFit(ImageFit.Contain);
} else {
Text(this.sharedData.getRecords()[0].content as string).width('100%').padding(8).backgroundColor('#F5F5F5').fontSize(14);
}
}.width('100%');
Text('选择联系人:').fontSize(14).color('#666').alignSelf('flex-start');
List() {
ForEach(mockContacts, (contact) => {
ListItem() {
Flex({ space: 12, alignItems: 'center' }) {
Image(contact.avatar).width(40).height(40).borderRadius(20);
Text(contact.name).fontSize(16);
}.padding(12).width('100%').onClick(() => this.sendToContact(contact));
};
});
}.height(200).width('100%');
Button('立即发送').width('100%').height(45).backgroundColor('#007AFF').onClick(() => {
if (this.contactInfo) {
this.sendToContact({ id: this.contactInfo.contactId, name: this.contactInfo.name });
} else {
promptAction.showToast({ message: '请选择联系人' });
}
});
}.padding(15).width('100%');
}
private sendToContact(contact: { id: string; name: string }) {
try {
console.info(`发送给 ${contact.name},ID:${contact.id}`);
promptAction.showToast({ message: `发送给 ${contact.name} 成功` });
this.extension.closePanel(systemShare.ShareAbilityResultCode.CLOSE);
} catch (error) {
console.error(`发送失败:${(error as Error).message}`);
promptAction.showToast({ message: '发送失败' });
this.extension.closePanel(systemShare.ShareAbilityResultCode.ERROR);
}
}
}
3.2 分享详情页返回逻辑控制
通过 terminateSelfWithResult 方法设置 resultCode,可控制分享面板的后续行为:
| ResultCode | 效果 | 适用场景 |
|---|
BACK | 正常返回分享面板 | 用户取消操作,需保留原分享面板 |
CLOSE | 直接关闭分享面板 | 分享成功,无需返回面板 |
ERROR | 返回面板并显示错误提示 | 分享失败(如网络异常),需告知用户 |
四、进阶功能:共享联系人到分享推荐区
社交类应用可通过意图框架(Intents Kit)将用户常用联系人共享到系统分享面板的'推荐区',用户无需在应用内二次选择,可一步完成分享,大幅提升操作效率。
4.1 前置条件
- 需申请意图框架白名单(仅开放给部分开发者,参考华为开发者联盟官方文档)。
- 联系人头像需转为 Base64 格式(避免路径访问权限问题)。
4.2 完整实现代码
import { util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { insightIntent } from '@kit.IntentsKit';
import { common } from '@kit.AbilityKit';
import BuildProfile from 'BuildProfile';
async function shareContactsToRecommendArea() {
try {
const uiContext = getContext(this) as common.UIContext;
const context = uiContext.getHostContext() as common.Context;
const contacts = [
{ contactId: 'user_1001', name: '小明', avatarBase64: 'data:image/png;base64,...', phone: '13800138000' },
{ contactId: 'user_1002', name: '小红', avatarBase64: 'data:image/png;base64,...', phone: '13900139000' }
];
const intents = contacts.map(contact => ({
intentName: 'SendMessage',
intentVersion: '1.0',
identifier: util.generateRandomUUID(),
intentActionInfo: {
actionMode: 'EXECUTED',
executedTimeSlots: {
executedStartTime: new Date().getTime(),
executedEndTime: new Date().getTime()
}
},
intentEntityInfo: {
entityId: contact.contactId,
entityName: 'Contact',
name: contact.name,
icon: contact.avatarBase64,
phoneNumbers: [contact.phone],
extras: {
shareParams: {
bundleName: BuildProfile.BUNDLE_NAME,
moduleName: 'entry',
abilityName: 'ShareExtensionAbility',
action: 'ohos.want.action.sendData'
}
}
}
}));
await insightIntent.shareIntent(context, intents);
console.info('联系人共享到分享推荐区成功');
} catch (error) {
const err = error as BusinessError;
console.error(`联系人共享失败:Code=${err.code}`);
}
}
@Component
struct HomePage {
onPageShow() {
this.shareContactsToRecommendArea();
}
build() {
// 应用首页 UI
}
}
4.3 关键说明
- 联系人更新时机:建议在应用启动、联系人列表变更时调用
shareIntent,确保推荐区的联系人信息最新。
- 隐私合规:共享联系人前需获取用户授权,明确告知用户'联系人信息将用于分享推荐',避免隐私合规风险。
- 白名单申请:意图框架白名单需通过华为开发者联盟申请,申请时需说明应用场景(如'社交应用联系人分享推荐')。
五、目标应用设计规范(必看,影响上架)
为保证用户在不同应用间的分享体验一致,华为对目标应用的图标、名称等有明确设计规范,违反规范可能导致应用上架被拒。
5.1 核心设计规范
| 配置项 | 规范要求 | 示例 |
|---|
应用图标(icon) | 1. 格式:PNG 或 SVG; 2. 尺寸:建议 48x48px; 3. 风格:与系统图标风格一致(圆角、扁平化) | 使用系统图标库或符合 HarmonyOS 设计语言的自定义图标 |
应用名称(label) | 1. 长度:不超过 8 个汉字或 16 个英文字符; 2. 语义:明确表达分享功能 | 正确:'发送到微信''保存到图库'; 错误:'分享''打开' |
| 多 Ability 配置 | 若应用有多个处理分享的 Ability,需为每个 Ability 配置独立的 icon 和 label | 两个 Ability 分别配置为'保存到相册'(图库图标)和'发送给好友'(社交图标) |
5.2 配置示例(符合规范)
{
"module": {
"abilities": [
{
"name": "SaveToGalleryAbility",
"label": "$string:save_to_gallery",
"icon": "$media:gallery_icon"
}
],
"extensionAbilities": [
{
"name": "SendToFriendAbility",
"label": "$string:send_to_friend",
"icon": "$media:friend_icon"
}
]
}
}
六、常见问题与排查方案
| 问题场景 | 可能原因 | 解决方案 |
|---|
| 目标应用未出现在分享面板 | 1. module.json5 中 actions 未设为 ohos.want.action.sendData; 2. UTD 类型配置错误; 3. exported 未设为 true。 | 1. 检查 actions 配置; 2. 确认 UTD 类型与分享内容一致; 3. 设 exported: true。 |
| 解析分享数据时提示'权限不足' | 1. 分享数据 URI 路径不可访问; 2. 目标应用缺少文件读取权限。 | 1. 宿主应用需将分享文件保存到沙箱; 2. 目标应用添加 ohos.permission.READ_USER_STORAGE 权限。 |
| 联系人未显示在分享推荐区 | 1. 未申请意图框架白名单; 2. 联系人数据格式错误; 3. 未获取用户隐私授权。 | 1. 完成白名单申请; 2. 检查 intentName 为 SendMessage; 3. 添加联系人授权弹窗。 |
| 分享详情页无法关闭 | 未调用 terminateSelfWithResult 方法,或 session 实例为 null。 | 确保在操作完成后调用 session?.terminateSelfWithResult,并在 onSessionCreate 中保存 session 实例。 |
七、总结
本文聚焦目标应用的全流程接入,从两种核心接入方式(UIAbility/ShareExtensionAbility),到联系人推荐、设计规范,覆盖了分享链路的'接收端'关键能力。关键要点总结如下:
- 接入方式选择:复杂处理用 UIAbility(独立页面),快速操作用 ShareExtensionAbility(嵌入式弹窗)。
- 配置核心:
module.json5 中 actions 必须为 ohos.want.action.sendData,UTD 类型需精准匹配业务需求。
- 用户体验:ShareExtensionAbility 支持返回面板控制,联系人推荐可实现'一步分享',大幅提升效率。
- 合规性:严格遵守设计规范和隐私合规要求,避免上架风险。
至此,我们已完成 HarmonyOS Share Kit 从'发起分享'到'接收处理'的全链路讲解。无论是宿主应用的定制化分享,还是目标应用的高效接入,Share Kit 都提供了灵活且统一的接口,帮助我们快速构建跨应用、跨设备的分享体验。如需进一步深入,可参考华为开发者联盟的目标应用接入示例工程,探索更多复杂场景的实现。
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online