Share Kit + HarmonyOS 文本分享:从信息脱敏到接收边界【共创稿事节】
一个“分享”按钮看起来只需要两行代码:拼一段文字、拉起系统面板。然而真正令工程团队反复修补的,并不是面板能否弹出,而是分享出去的那段内容究竟由谁决定。如果应用把页面当前对象直接序列化,就可能把用户标识、内部备注、尚未公开的草稿链接一并交给外部应用。分享一旦离开原来的应用边界,收回数据往往比撤回一次页面操作难得多。
我更赞同把分享看作一次主动的数据出境决策。系统负责呈现接收目标和执行后续交互,业务负责选择最小且明确的共享内容。两边职责不相互替代。HarmonyOS 官方“分享文本”文档提供了公开的 Share Kit 接入方式,因此这篇文章只讨论普通文本与公开链接,不把设备“碰一碰”类体验宣传直接推断为可以调用的专有开放 API,也不混用面向 Android 的 Share Engine 套件。

1. 分享对象不等于页面对象
以知识阅读应用为例,一条文章记录在数据库中可能有标题、正文、内部分类、编辑人员、用户的阅读位置、访问凭据和公开链接。真正适合外发的通常只有“可公开的标题”与“经过审核的公共访问地址”。就算字段都存放在同一个 ArkTS 类型中,公开范围也完全不同。
工程中常见的错误,是点击按钮时把整个 ArticleDetail 转成 JSON,再交给分享接口。这样的代码测试起来很方便,却把未来新增的字段默默加入了外发范围:业务后来加了“审核原因”或“客户渠道”,分享能力不修改一行代码也会扩散更多数据。相反,白名单方案明确构造一份 PublicShareItem,新业务字段不会自动成为公开字段。
这里的“链接”还有另一层风险。页面路由可能带着临时签名、访问 token、来源追踪参数;这些参数对当前用户有用,不代表应当被写进系统分享面板。不要靠简单字符串替换删除 token=,因为敏感信息可能换了参数名,也可能藏在 URL 片段中。更稳妥的方式是由业务系统提供独立的公开链接,客户端只接受指定域名和规范路径,拒绝带查询串、片段或跳转域名的原始页面地址。
2. 用公开类型表达同意分享的内容
下面给出一个独立的 ArkUI 页面示例。它不需要引入三方网络包,调用官方 @kit.ShareKit 的 systemShare.SharedData 和 ShareController;文本类型通过 @kit.ArkData 的 UniformDataType.TEXT 指定。为方便观察异常,组件提供一个状态提示区。实际应用可把文章列表和详情页替换为自己的数据源。
import { systemShare } from '@kit.ShareKit';
import { uniformTypeDescriptor as utd } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
class PublicShareItem {
title: string;
publicUrl: string;
constructor(title: string, publicUrl: string) {
this.title = title;
this.publicUrl = publicUrl;
}
}
class TextSharePolicy {
// 正式项目替换成已备案、由业务服务提供的真实公开链接域名。
private static readonly prefix: string = 'https://docs.example.com/read/';
static build(item: PublicShareItem): string | null {
const title: string = item.title.trim();
if (title.length === 0 || title.length > 80 || /[\r\n]/.test(title)) {
return null;
}
if (!item.publicUrl.startsWith(TextSharePolicy.prefix)) {
return null;
}
const slug: string = item.publicUrl.substring(TextSharePolicy.prefix.length);
if (!/^[A-Za-z0-9_-]{1,64}$/.test(slug)) {
return null;
}
return `推荐阅读:${title}\n${item.publicUrl}`;
}
}
@Entry
@Component
struct ShareEntry {
@State status: string = '可分享公开文章摘要';
private article: PublicShareItem = new PublicShareItem(
'ArkUI 分栏阅读的布局策略',
'https://docs.example.com/read/arkui-layout'
);
private async openSharePanel(): Promise<void> {
const text: string | null = TextSharePolicy.build(this.article);
if (text === null) {
this.status = '分享被阻止:公开字段不符合规则';
return;
}
const data: systemShare.SharedData = new systemShare.SharedData({
utd: utd.UniformDataType.TEXT,
content: text,
title: '公开文章摘要',
description: '分享标题与公共阅读链接'
});
const controller: systemShare.ShareController =
new systemShare.ShareController(data);
const uiContext: UIContext = this.getUIContext();
const context: common.UIAbilityContext =
uiContext.getHostContext() as common.UIAbilityContext;
try {
await controller.show(context, {
selectionMode: systemShare.SelectionMode.SINGLE,
previewMode: systemShare.SharePreviewMode.DETAIL
});
this.status = '分享面板已打开,请选择接收目标';
} catch (error) {
const err: BusinessError = error as BusinessError;
this.status = `分享面板启动失败(${err.code})`;
}
}
build() {
Column({ space: 18 }) {
Text(this.article.title).fontSize(23).fontWeight(FontWeight.Bold)
Text('仅分享公开标题及规范链接').fontSize(15)
Button('打开系统分享面板')
.onClick(() => { this.openSharePanel(); })
Text(this.status).fontSize(14)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(24)
}
}
docs.example.com 是代码中的示例公开域名,部署时应改为实际受控且可以访问的地址。这里把 URL 的路径限制为字母、数字、连字符和下划线,能排除查询串与片段,却并不意味着内容本身已经经过公开审批。因此生产环境仍应由服务端校验资源的可公开状态;客户端只是第二道约束,不应凭一个格式正确的链接绕过内容权限。
还有一个容易误读的地方:ShareController.show() 完成意味着分享面板显示流程完成,并不等于消息已经被对方接收,更不等于跨设备传输成功。埋点名称和页面提示应该反映这一事实,不能拿一次“面板打开”当作传播成功的统计口径。

3. 分享出去之前,内容要经历三次检查
我倾向于将分享校验分成“资源身份—字段范围—交付目标”三层。资源身份检查确认文章确实可以公开;字段范围检查只生成标题与公开链接;交付目标由用户在系统面板中决定。这种结构的好处是,即使以后更换 UI、加入卡片预览或调整推荐文案,数据规则仍然可以保持稳定。
第一层发生在业务数据获得时。尚未发布的文章、只允许团队阅读的知识库以及需要登录才能访问的资源,默认不能拿来生成公开分享链接。服务端应该返回显式的可分享状态或专用共享地址,不应把内部详情 URL 直接透传给页面。第二层发生在点击分享按钮后:检查文本长度、换行符、允许的域名和路径格式,并把待分享内容先展示给用户确认。如果包含姓名、手机号或账户信息,应由业务流程决定拒绝或按授权规则处理,而不是使用一个简单的正则就自称全面完成脱敏。
第三层并不是让应用偷偷决定对端。系统分享面板应由用户明确操作,应用不预选敏感联系人,也不把分享到外部应用后的读取行为当成自己能够控制的事情。用户选择取消、没有可用目标,或者系统拒绝启动面板,界面都要能继续使用。重试分享不得把上一轮残留的内容与下一篇文章混在一起。

4. 为什么我不建议把“碰一碰精准分享”作为默认实现
产品宣传中的近场交互与开发者 SDK 的公开接口不是同一层概念。有些能力受设备型号、系统版本、业务接入资质或特定场景限制。当前工程只需要将公开文本交给系统选项,因此依照 Share Kit 的 SharedData、ShareController、UTD 类型体系实现,接入依据明确,也更容易测试。若后来确实需要设备近场互传,应再逐项核实对应公开 SDK、设备范围与权限,而不是凭一个产品展示推断出类名和方法名。
同样不能把华为面向 Android 等平台的 Share Engine 文档直接当成 HarmonyOS Stage 应用的 @kit.ShareKit 接口文档。两套能力的目标、支持平台和工程集成路线有差别。复制一段看起来包含“Share”字样的示例并不能证明它适用于当前构建模型。对开发者来说,选择更窄、证据更清楚的开放能力,反而能减少无法维护的适配代码。
对文本分享还要警惕“完成反馈”的设计。系统面板可以为用户提供目标列表,但接收应用对数据如何展示、保存、转发由其自身处理。原应用无法通过本例接口强制对端销毁副本,不能将“撤回分享”设计成完全可靠的控制。真正需要撤销访问时,应依赖服务端受控链接的失效机制,而不只是隐藏一个本地按钮。
5. 验收时重点看被拒绝的输入
一套分享能力至少应覆盖这些检查:公开文章正常弹出面板;私密文章没有公共链接时拒绝;标题为空或包含换行时拒绝;URL 中出现问号、井号、错误主机名或路径穿越时拒绝;系统面板调用出错后页面继续可操作;用户反复点击时不会把上一篇的内容带到下一篇。测试账号和测试内容应使用可公开的数据,日志中只记录错误码与必要的操作状态,不输出完整分享文本。
最值得保留的一条原则是:系统分享接口解决“如何交付”,业务白名单解决“什么允许交付”。 前者由系统提供可靠入口,后者必须由应用开发者负责。把两个问题拆开,分享功能才不会在业务不断增加字段之后悄悄扩大隐私边界。
参考资料:
更多推荐


所有评论(0)