都 2025 了,还在自己 app 里关起门来玩数据?不如让 DataAbility 替你‘通关’!
本文介绍了鸿蒙系统中DataAbility的应用间数据共享机制。DataAbility通过URI标识、权限校验和增删改查接口,为应用提供系统化的数据共享方案。文章详细解析了其架构设计,包括提供方(DataAbility)和访问方(DataAbilityHelper)的交互流程,并对比了FA模型和Stage模型下的不同实现方式。同时重点阐述了URI规范、权限控制机制(静态配置与动态校验)以及实战案例
我是兰瓶Coding,一枚刚踏入鸿蒙领域的转型小白,原是移动开发中级,如下是我学习笔记《零基础学鸿蒙》,若对你所有帮助,还请不吝啬的给个大大的赞~
前言
先从一个略带吐槽的场景开场:你有个记事/账本/收藏类应用,用户说:“我还有一款工具 App,也想直接读你的‘条目列表’,别让我再导入导出了,行不?”这时候,如果你还想着“我给你丢个 JSON、或者开个本地 HTTP 端口”,那大概率会陷入权限难校验、协议难迭代、兼容性难维持的三难困境。
鸿蒙体系里,这活儿其实该交给 DataAbility:它是系统化的应用间数据共享机制,天然支持URI 标识、权限校验、增删改查(insert/query/update/delete),客户端只需要一个 DataAbilityHelper 就能像用数据库一样“取数/写数”。官方还给了完整架构与 API,上手并不难(等会儿就用代码把它跑起来)。(华为开发者官网)
友情提示:如果你的项目是 Stage 模型优先、而且目标是“新版本” HarmonyOS,官方同时提供了 DataShare(DataShareExtensionAbility + DataShare 接口族)作为跨应用数据共享的“Stage 路线”。本文主轴仍按你给的主题讲 DataAbility,但会在合适位置标注 Stage 侧的“等价思路”,方便你迁移或二选一。(华为开发者官网)
一、DataAbility 架构:谁提供、谁访问、怎么跑
1) 两个主角:提供方 与 访问方
- 提供方(Provider):实现一个 DataAbility 组件,内部可接 RDB(关系数据库)、文件、内存表等,把对外能力封装为 增删改查接口;
- 访问方(Consumer):通过 DataAbilityHelper,携带 URI,调用
insert/query/update/delete等操作。(华为开发者官网)
一个“串得起来的脑图”——
┌──────────────┐ URI(dataability://…)
│ 访问方 App A │ ──────────────────────────┐
│ DataAbility │ ──(DataAbilityHelper)──▶ │ ┌──────────────────────┐
│ Helper │ └▶ │ 提供方 App B │
└──────────────┘ │ DataAbility │
▲ │ (RDB/File/内存…) │
│ (结果/游标/计数) │ insert/query/update │
└────────────────────────────────────────┴──────────────────────┘
要点
- 全流程以 URI 为“寻址核心”,调用前不需要绑定服务,这让“跨应用取数”具有低耦合/强约束的特性。
- 提供方是否可被访问,由 组件配置 与 权限校验共同决定(后文专讲)。(华为开发者官网)
2) 关于 FA / Stage 的“路线图”
- FA 模型里就是 DataAbility + DataAbilityHelper 这对组合;
- Stage 模型里推荐 DataShareExtensionAbility + DataShare / DataShareHelper 完成同类能力(命名不同、思路一致)。如果新项目直接走 Stage,可优先考虑 DataShare。(华为开发者官网)
二、URI 机制:一串字符串,决定你访问的是谁的什么
1) URI 规范与示例
官方对 DataAbility 的 URI 定义非常清晰:
- scheme:固定为
"dataability"; - authority:设备 ID(跨设备时填写;本地设备为空,因此紧跟着是
///); - path:资源路径(如表/集合/条目);
- query/fragment:可选,用于过滤/定位子资源。
示例: - 本地设备:
dataability:///com.example.note.provider/notes/1 - 跨设备:
dataability://{device_id}/com.example.note.provider/notes/10
(本地因为 device_id 为空,所以dataability:后有 三个斜杠,这点很多同学第一次会写错。)(华为开发者官网)
2) 组件声明中的 uri 与 visible
在 config.json(或等价的模块配置)里,提供方需要给 DataAbility 声明一个基础 uri,以及是否对外可见:
"type": "data"(标识它是 DataAbility);"uri": "dataability:///com.example.note.provider";"visible": true(对其他应用可见,才能被外部访问)。(华为开发者官网)
Tips:URI 只是“入口前缀”,你依旧可以在 path 里细化到表/行级路径,例如
/notes、/notes/123等,配合谓词实现复杂筛选。
三、数据访问权限:静态 + 动态,双保险
DataAbility 的权限控制分两层:
- 静态权限(安装前/拉起时检查):在 config.json 中配置
readPermission/writePermission; - 动态权限(真正操作时检查):按接口类型判断“读/写”权限是否满足。官方文档明确说明了这两块。(华为开发者官网)
1) 静态权限(配置在提供方)
{
"module": {
"abilities": [
{
"name": ".NoteDataAbility",
"type": "data",
"uri": "dataability:///com.example.note.provider",
"visible": true,
"readPermission": "ohos.permission.READ_NOTES",
"writePermission": "ohos.permission.WRITE_NOTES"
}
]
}
}
- 含义:没有
READ_NOTES的客户端,拉起后也读不到;没有WRITE_NOTES的客户端,不能改。 - 注意:客户端还需要在自身的配置里 声明使用这些权限(通常是
requestPermissions/ 安装授权等),否则会出现常见的 Permission Denied。(华为开发者官网)
2) 动态权限(按接口“读/写”判定)
- 读权限接口:
query/normalizeUri/denormalizeUri/openFile('r')等; - 写权限接口:
insert/batchInsert/update/delete/openFile('w')等; - 有的接口(如
executeBatch)会按子操作判定权限。具体映射在官方/社区资料均有一致说明。(华为开发者官网)
经验谈:读写分权尽量设计得“开口小”一些;真的需要“可写”的访问方,要么签白名单、要么多一层业务网关,别把数据表“裸暴露”。
四、实战|从 0 写一个可共享的 Note DataAbility
需求:提供方 App 暴露一个“便签表”,可被外部 App 插入/查询/更新。内部用 RDB 存储。
1) 提供方:DataAbility(实现 insert/query/update)
// src/main/ets/ability/NoteDataAbility.ets
import dataAbility from '@ohos.data.dataAbility'
import rdb from '@ohos.data.rdb'
import hilog from '@ohos.hilog'
const TAG = 'NoteDataAbility'
const TABLE = 'notes'
export default class NoteDataAbility extends dataAbility.DataAbility {
private store?: rdb.RdbStore
async onInitialize() {
hilog.info(0x0, TAG, 'onInitialize')
const config: rdb.StoreConfig = { name: 'notes.db', securityLevel: rdb.SecurityLevel.S1 }
this.store = await rdb.getRdbStore(this.context, config, 1, (db: rdb.RdbStore) => {
db.executeSql(`CREATE TABLE IF NOT EXISTS ${TABLE} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
updated_at INTEGER
)`)
})
}
// INSERT
async insert(uri: string, value: dataAbility.ValuesBucket): Promise<number> {
this.assertWritable('insert')
const now = Date.now()
value.putLong('updated_at', now)
const row = await this.store!.insert(TABLE, value)
hilog.info(0x0, TAG, `insert row=${row}`)
// 通知变更(可选)
this.notifyChange(uri)
return row
}
// QUERY
async query(
uri: string,
predicates: dataAbility.DataAbilityPredicates,
columns: Array<string>
): Promise<dataAbility.ResultSet> {
this.assertReadable('query')
const rs = await this.store!.query(TABLE, columns.length ? columns : ['*'], predicates)
return rs
}
// UPDATE
async update(
uri: string,
value: dataAbility.ValuesBucket,
predicates: dataAbility.DataAbilityPredicates
): Promise<number> {
this.assertWritable('update')
value.putLong('updated_at', Date.now())
const changed = await this.store!.update(value, TABLE, predicates)
this.notifyChange(uri)
return changed
}
// (需要的话再实现 delete/batchInsert/executeBatch)
private assertReadable(op: string) { if (!this.store) throw new Error(`Store not ready: ${op}`) }
private assertWritable(op: string) { if (!this.store) throw new Error(`Store not ready: ${op}`) }
}
你会发现:DataAbility 的形态跟“数据库 DAO 层”很像,但它的入口从函数变成了 URI + 方法,从而满足“跨应用调用”的约束。官方“创建 DataAbility”的文档也强调了实现 insert/query/update/delete 即可覆盖大多数场景,批量操作(
batchInsert/executeBatch)会基于这些基础能力完成遍历。(华为开发者官网)
2) 访问方:DataAbilityHelper 的“增、查、改”
// src/main/ets/model/NoteRepo.ets
import featureAbility from '@ohos.ability.featureAbility' // 获取 DataAbilityHelper
import dataAbility from '@ohos.data.dataAbility'
const NOTE_URI = 'dataability:///com.example.note.provider/notes'
export class NoteRepo {
private helper?: dataAbility.DataAbilityHelper
async init() {
this.helper = featureAbility.acquireDataAbilityHelper(NOTE_URI)
}
async add(title: string, content: string) {
const v = new dataAbility.ValuesBucket()
v.putString('title', title)
v.putString('content', content)
const rowId = await this.helper!.insert(NOTE_URI, v)
return rowId
}
async list(keyword = ''): Promise<Array<{ id: number; title: string; content: string }>> {
const pred = new dataAbility.DataAbilityPredicates()
if (keyword) pred.like('title', `%${keyword}%`)
const rs = await this.helper!.query(NOTE_URI, pred, ['id', 'title', 'content'])
const out: any[] = []
while (rs.goToNextRow()) {
out.push({
id: rs.getLong(rs.getColumnIndex('id')),
title: rs.getString(rs.getColumnIndex('title')),
content: rs.getString(rs.getColumnIndex('content')),
})
}
rs.close()
return out
}
async rename(id: number, newTitle: string) {
const v = new dataAbility.ValuesBucket()
v.putString('title', newTitle)
const pred = new dataAbility.DataAbilityPredicates().equalTo('id', id)
const changed = await this.helper!.update(NOTE_URI, v, pred)
return changed
}
}
这一套 DataAbilityHelper 的获取与调用方式,是官方 API 的标准范式:
acquireDataAbilityHelper(uri) → insert/query/update/delete。你可以把它当成“定位到某个 provider 的数据端点,再执行操作”。(华为开发者官网)
五、把“URI 机制 + 权限 + 读写”拉通,再谈跨设备
DataAbility 的 URI 天生支持 跨设备寻址(dataability://{device_id}/...)。要真正跨设备访问,前置依赖是设备间可信关系/组网等协同前提,这通常由系统协同层完成;就 URI 语义而言,写法如上一节所示。具体到你项目:
- 若你的应用群已覆盖多设备(手机/平板/PC/…),且存在同账号可信,就可以按目标设备 ID 构造 URI;
- 数据层仍按 insert/query/update 调用,只不过“路由”到了另一台设备上的 DataAbility。
URI 写法与配置见官方 DataAbility 组件配置与 URI 说明。(华为开发者官网)
六、工程级实践:可维护、可演进、可上线
1) 版本/模型选择建议
- 全新 Stage 项目:若你一开始就走 Stage,可以评估 DataShare 体系(服务端用 DataShareExtensionAbility,客户端用
dataShare模块),在“Stage-only 环境”里更贴新版 API。(华为开发者官网) - 既有 FA 项目:优先按本文方案实现 DataAbility;如果未来切 Stage,再在“URI & 调用层”做一层适配封装,把“数据端点”抽象出来,避免散落在业务代码里逐个替换。
2) 协议 & 约束:不要把表结构直接裸露成接口
- 声明一个稳定的“数据契约”(表名、列名、可过滤字段、返回列清单),像“接口文档”一样发给接入方;
- 对外只公开必要列,敏感字段别公开(或返回脱敏值);
- 统一失败码/错误信息(例如
PERMISSION_DENIED、ILLEGAL_ARGUMENT、NOT_FOUND),方便对方排查。
3) 性能与稳定性
- 分页/游标:大表查询一定要分页;
- 变更通知:写操作后
notifyChange(uri),让订阅方收到更新,少轮询; - 索引:对查询高频的列(如
title)建索引,不要让对方“搜一次卡半天”; - 崩溃兜底:insert/update 里永远做输入校验,避免对方传个奇怪的值把你库“打花”。
4) 安全复盘
- 静态权限 + 动态权限 双重校验,拒绝“偷着写”;
- 对写操作记录 审计日志(调用方包名、时间、影响行数);
- 若涉及用户隐私,二次确认/告知机制别省。
七、常见坑位(都是“被 Permission Denied 打醒”后记下的)
visible忘了设true:外部 App 永远拉不到你这端;- URI 拼错:尤其是本地设备
dataability:///的 三个斜杠; - 只配了静态权限,客户端没声明/没授权:拉起能拉起,操作还是 Permission Denied;
- ResultSet 没
close():内存与句柄泄漏; - 值类型与列类型不匹配:比如把时间戳当字符串塞,查询排序全乱;
- 忘了索引:模糊搜索一打就“半分钟没回音”。
第 3 条最常见,官方/社区问答里也反复强调“服务端的 read/writePermission + 客户端的权限声明”要同时满足。(华为开发者官网)
八、(可选)把这套“对外数据层”做成可迁移的“适配器”
目标:无论 DataAbility(FA)还是 DataShare(Stage),上层业务只面向一个“数据端点接口”。
// adapter/Endpoint.ts
export interface Endpoint {
insert(path: string, values: Record<string, any>): Promise<number>
query(path: string, where?: { [k: string]: any }, columns?: string[]): Promise<any[]>
update(path: string, values: Record<string, any>, where?: { [k: string]: any }): Promise<number>
}
- DataAbilityEndpoint:内部用
DataAbilityHelper; - DataShareEndpoint:内部用
dataShare的 publish/get/update 等;
这样你能在一个开关下切换实现,迁移成本立刻可控。(DataShare API 参考官方文档)(华为开发者官网)
九、完整最小示例(清单)
-
Provider(提供方 App)
-
Consumer(访问方 App)
manifest:声明使用ohos.permission.READ_NOTES/WRITE_NOTES;NoteRepo.ets:acquireDataAbilityHelper→insert/query/update。(华为开发者官网)
十、收个尾:把“共享数据”当成 产品能力,而不是“临时通道”
我最喜欢 DataAbility 的地方,是它把“跨应用数据共享”从“个案协商”变成了“平台化契约”:URI 可寻址、权限可校验、接口有边界。你不需要在每个接入方面前再解释一次“我这张表长啥样、我这口子怎么开”,用稳定的 URI + 权限模型 + 增删改查就能把合作关系变得可维护。
参考(要点直达)
- DataAbilityHelper 官方 API(获取 Helper 并执行 insert/query/update 等)。(华为开发者官网)
- 创建 DataAbility(实现 insert/query/update/delete,批量操作依赖基础 CRUD)。(华为开发者官网)
- DataAbility 组件配置与 URI 说明(
dataabilityscheme、device_id、visible、uri等)。(华为开发者官网) - DataAbility 权限控制(静态
readPermission/writePermission+ 动态按接口校验)。(华为开发者官网) - DataShare(Stage 路线)API(Stage 模型下的数据共享接口族,可作为 DataAbility 的替代方案)。(华为开发者官网)
…
(未完待续)
更多推荐



所有评论(0)