Administrator
发布于 2026-09-03 / 10,004 阅读
0
0

鸿蒙 IPC 跨进程通信:DataShareExtension 与跨应用数据流转

系列文章《鸿蒙开发从入门到专家》第 56 篇 · 高级

前面讲的都是应用内部的事。这篇讲应用之间:A 应用怎么读写 B 应用的数据,跨进程通信在鸿蒙里怎么实现。

鸿蒙的跨进程通信不是直接调 Binder(Android 的方式),而是通过系统提供的 ExtensionAbility 框架。其中 DataShareExtensionAbility 是专门用于跨应用数据共享的扩展能力。

一、为什么需要跨进程通信

几个实际场景:

  1. 输入法应用需要读取联系人应用的通讯录数据。

  2. 系统设置应用需要修改壁纸应用提供的图片列表。

  3. 企业应用 A 需要获取应用 B 维护的审批流程数据。

  4. 多个应用共享同一份配置(如公司内部所有应用共享登录态)。

这些场景的共同点:数据所有者是应用 B,应用 A 需要访问。直接读 B 的沙盒文件不可能(应用沙盒隔离),需要通过系统提供的安全通道。

二、DataShareExtension 架构

┌──────────────────────────────────────────────────────┐
│                  数据访问方(应用 A)                 │
│                                                      │
│  DataShareHelper                                     │
│  ├── createDataShareHelper(uri)                      │
│  ├── query(uri, predicates, columns)                │
│  ├── insert(uri, value)                              │
│  ├── update(uri, predicates, value)                 │
│  └── delete(uri, predicates)                        │
│                                                      │
├──────────────── IPC 通信 ────────────────────────────┤
│                                                      │
├──────────────────────────────────────────────────────┤
│              数据提供方(应用 B)                      │
│                                                      │
│  DataShareExtensionAbility                           │
│  ├── onCreate(want, callback)     ← 初始化           │
│  ├── query(uri, predicates, ...)  ← 查询             │
│  ├── insert(uri, value, ...)      ← 插入             │
│  ├── update(uri, predicates, ...) ← 更新            │
│  ├── delete(uri, predicates, ...)  ← 删除           │
│  └── batchInsert(...)              ← 批量插入        │
│                                                      │
│  底层数据存储(RDB / 文件 / 内存)                     │
└──────────────────────────────────────────────────────┘

数据提供方(应用 B)实现 DataShareExtensionAbility,对外暴露增删改查接口。数据访问方(应用 A)通过 DataShareHelper 创建连接,像操作数据库一样操作对方的数据。中间通过 IPC 完成跨进程通信。

需要注意:DataShareExtensionAbility 的数据提供方目前仅限系统应用。第三方应用可以作为数据访问方使用 DataShareHelper,但不能自己实现 DataShareExtensionAbility 提供数据。

三、数据提供方实现

3.1 创建 ExtensionAbility

// DataShareExtAbility.ets
import { DataShareExtensionAbility, dataShare, dataSharePredicates,
  relationalStore, DataShareResultSet } from '@kit.ArkData';
import { Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const DB_NAME = 'shared_data.db';
const TBL_NAME = 'contacts';
const DDL = `CREATE TABLE IF NOT EXISTS ${TBL_NAME} (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  phone TEXT NOT NULL,
  email TEXT
)`;

let rdbStore: relationalStore.RdbStore;

export default class DataShareExtAbility extends DataShareExtensionAbility {
  // 初始化:创建数据库和表
  onCreate(want: Want, callback: Function): void {
    hilog.info(0x0000, 'DataShare', 'onCreate');

    relationalStore.getRdbStore(this.context, {
      name: DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S3
    }, (err: BusinessError, data: relationalStore.RdbStore) => {
      if (err) {
        hilog.error(0x0000, 'DataShare', 'getRdbStore failed: %{public}s', err.message);
        callback();
        return;
      }
      rdbStore = data;
      rdbStore.executeSql(DDL, [], (err) => {
        if (err) {
          hilog.error(0x0000, 'DataShare', 'executeSql failed: %{public}s', err.message);
        }
        callback();
      });
    });
  }

  // 查询
  query(uri: string, predicates: dataSharePredicates.DataSharePredicates,
    columns: Array<string>, callback: Function): void {
    hilog.info(0x0000, 'DataShare', 'query: %{public}s', uri);

    if (!rdbStore) {
      callback(null, null);
      return;
    }

    rdbStore.query(TBL_NAME, predicates, columns, (err, resultSet) => {
      if (err) {
        hilog.error(0x0000, 'DataShare', 'query failed: %{public}s', err.message);
        callback(err, null);
      } else {
        callback(null, resultSet);
      }
    });
  }

  // 插入
  insert(uri: string, value: relationalStore.ValuesBucket,
    callback: Function): void {
    hilog.info(0x0000, 'DataShare', 'insert: %{public}s', uri);

    rdbStore.insert(TBL_NAME, value, (err, rowId) => {
      if (err) {
        callback(err, -1);
      } else {
        callback(null, rowId);
      }
    });
  }

  // 更新
  update(uri: string, predicates: dataSharePredicates.DataSharePredicates,
    value: relationalStore.ValuesBucket, callback: Function): void {
    rdbStore.update(TBL_NAME, value, predicates, (err, rowId) => {
      callback(err, rowId);
    });
  }

  // 删除
  delete(uri: string, predicates: dataSharePredicates.DataSharePredicates,
    callback: Function): void {
    rdbStore.delete(TBL_NAME, predicates, (err, rowId) => {
      callback(err, rowId);
    });
  }
}

3.2 配置 module.json5

{
  "module": {
    "name": "provider_app",
    "type": "feature",
    "extensionAbilities": [
      {
        "name": "DataShareExtAbility",
        "type": "dataShare",
        "uri": "datashare://com.example.provider_app/contacts",
        "metadata": [
          {
            "name": "ohos.extension.dataShare",
            "resource": "$profile:data_share_config"
          }
        ]
      }
    ]
  }
}

data_share_config.json:

{
  "visibility": "private",
  "readPermission": "ohos.permission.READ_CONTACTS",
  "writePermission": "ohos.permission.WRITE_CONTACTS"
}
  • readPermission / writePermission:声明访问方需要持有的权限。

  • visibility:private 表示仅持有权限的应用可访问,public 表示所有应用可访问。

四、数据访问方实现

// consumer/ConsumerPage.ets
import { dataShare, dataSharePredicates } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

const PROVIDER_URI = 'datashare://com.example.provider_app/contacts';

@Entry
@Component
struct ConsumerPage {
  @State contacts: Contact[] = [];
  private helper: dataShare.DataShareHelper | null = null;

  async aboutToAppear(): Promise<void> {
    const context = getContext(this) as common.UIAbilityContext;

    // 创建 DataShareHelper
    this.helper = await dataShare.createDataShareHelper(context, PROVIDER_URI);

    // 查询所有联系人
    await this.queryContacts();
  }

  async queryContacts(): Promise<void> {
    if (!this.helper) return;

    const predicates = new dataSharePredicates.DataSharePredicates();
    const columns = ['id', 'name', 'phone', 'email'];

    const resultSet = await this.helper.query(PROVIDER_URI, predicates, columns);

    const list: Contact[] = [];
    while (resultSet.goToNextRow()) {
      list.push({
        id: resultSet.getLong(resultSet.getColumnIndex('id')),
        name: resultSet.getString(resultSet.getColumnIndex('name')),
        phone: resultSet.getString(resultSet.getColumnIndex('phone')),
        email: resultSet.getString(resultSet.getColumnIndex('email'))
      });
    }
    this.contacts = list;
    resultSet.close();
  }

  async addContact(name: string, phone: string): Promise<void> {
    if (!this.helper) return;

    const value: relationalStore.ValuesBucket = {
      'name': name,
      'phone': phone
    };

    await this.helper.insert(PROVIDER_URI, value);
    await this.queryContacts(); // 刷新列表
  }

  build() {
    Column() {
      // 列表渲染
      List() {
        ForEach(this.contacts, (item: Contact) => {
          ListItem() {
            Column() {
              Text(item.name).fontSize(16)
              Text(item.phone).fontSize(14).fontColor('#666')
            }
          }
        })
      }

      Button('添加联系人')
        .onClick(() => {
          this.addContact('张三', '13800001111');
        })
    }
  }
}

五、条件查询

DataSharePredicates 支持丰富的条件查询:

async searchContacts(keyword: string): Promise<void> {
  if (!this.helper) return;

  const predicates = new dataSharePredicates.DataSharePredicates();

  // WHERE name LIKE '%keyword%' OR phone LIKE '%keyword%'
  predicates.beginsWith('name', keyword);
  predicates.or();
  predicates.beginsWith('phone', keyword);

  // 排序
  predicates.orderByAsc('name');

  // 分页
  predicates.limitAs(20);
  predicates.offsetAs(0);

  const columns = ['id', 'name', 'phone', 'email'];
  const resultSet = await this.helper.query(PROVIDER_URI, predicates, columns);

  // ... 处理结果集
}

方法

SQL 等价

equalTo(col, val)

WHERE col = val

notEqualTo(col, val)

WHERE col != val

beginsWith(col, prefix)

WHERE col LIKE ‘prefix%’

contains(col, substr)

WHERE col LIKE ‘%substr%’

greaterThan(col, val)

WHERE col > val

lessThan(col, val)

WHERE col < val

between(col, low, high)

WHERE col BETWEEN low AND high

orderByAsc(col)

ORDER BY col ASC

limitAs(n) + offsetAs(m)

LIMIT n OFFSET m

六、权限处理

访问方在使用 DataShareHelper 之前,需要声明并请求对应权限:

// module.json5
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_CONTACTS",
        "reason": "$string:read_contacts_reason"
      },
      {
        "name": "ohos.permission.WRITE_CONTACTS",
        "reason": "$string:write_contacts_reason"
      }
    ]
  }
}

代码中动态请求:

import { abilityAccessCtrl } from '@kit.AbilityKit';

async function ensurePermissions(): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager();
  const context = getContext(this) as common.UIAbilityContext;

  const permissions = ['ohos.permission.READ_CONTACTS', 'ohos.permission.WRITE_CONTACTS'];

  try {
    const result = await atManager.requestPermissionsFromUser(context, permissions);
    return result.authStatuses.every(status => status === 0);
  } catch (e) {
    console.error('Permission request failed: ' + e);
    return false;
  }
}

七、对比其他跨进程方案

鸿蒙有多种跨进程/跨应用通信方式,各有适用场景:

方案

通信方式

适用场景

限制

DataShareExtension

数据 CRUD

跨应用数据共享

提供方限系统应用

CommonEvent

广播

系统级事件通知

不适合高频通信

Want + ExtensionAbility

拉起 + 回调

跨应用功能调用

需要用户感知

RPC (remoteObject)

远程对象调用

同设备进程间方法调用

需要手动管理连接

DataShareExtension 的优势在于:访问方像操作本地数据库一样操作远端数据,API 友好。劣势是只支持 CRUD 语义,不适合做实时消息推送或复杂方法调用。

八、踩坑记录

坑 1:URI 格式必须精确匹配。 DataShareHelper 的 URI 必须和提供方 module.json5 中声明的 URI 完全一致,包括 scheme(datashare://)和路径。少一个斜杠都连不上,且报错信息不明确。

坑 2:初始化异步等待。 onCreate 是异步的(创建数据库需要时间),如果在 onCreate 的 callback 执行之前就收到 query 请求,rdbStore 可能还没准备好。要在 onCreate 中确保 callback 被调用,框架才会认为初始化完成。

坑 3:结果集必须关闭。 DataShareResultSet 使用共享内存实现,不调用 close() 会导致内存泄漏。特别是在循环查询场景,每次查完都要 close。

坑 4:DataShareHelper 生命周期。 Helper 创建后会持有连接,页面销毁时需要释放。目前没有显式的 release 方法,Helper 对象被 GC 时自动断开连接。但如果 Helper 被长期持有(如放在单例中),连接不会断开。

小结

跨应用数据共享在鸿蒙里走 DataShareExtension + DataShareHelper 这条路。提供方实现 ExtensionAbility 的 CRUD 回调,访问方通过 Helper 像操作本地数据库一样操作远端数据。关键注意权限配置、URI 精确匹配和结果集关闭。第三方应用目前只能做访问方,提供方限系统应用。

下篇讲内存管理。Allocation 工具怎么用、泄漏怎么排查、组件生命周期怎么治理。

参考阅读:

  • 第 23 篇:鸿蒙 RDB 数据库进阶

  • 第 24 篇:鸿蒙文件管理与文档处理

  • 官方文档:DataShareExtensionAbility、DataShareHelper


评论