HarmonyOS学习路之开发篇——Data Ability 原创

爱吃土豆丝的打工人
发布于 2021-6-21 10:10
浏览
5收藏

Data Ability基本概念

使用Data模板的Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。

数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。

URI介绍

Data的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。HarmonyOS的URI仍基于URI通用标准,格式如下:

  • scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型
  • authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。
  • path:资源的路径信息,代表特定资源的位置信息。
  • query:查询参数。 f
  • ragment:可以用于指示要访问的子资源。

URI示例:

  • 跨设备场景:dataability://device_id/com.domainname.dataability.persondata/person/10
  • 本地设备:dataability:///com.domainname.dataability.persondata/person/10

创建Data

使用Data模板的Ability形式仍然是Ability,因此,开发者需要为应用添加一个或多个Ability的子类,来提供程序与其他应用之间的接口。Data为结构化数据和文件提供了不同API接口供用户使用,因此,开发者需要首先确定好使用何种类型的数据。本章节主要讲述了创建Data的基本步骤和需要使用的接口。
Data提供方可以自定义数据的增、删、改、查,以及文件打开等功能,并对外提供这些接口。

确定数据存储方式

确定数据的存储方式,Data支持以下两种数据形式:

  • 文件数据:如文本、图片、音乐等。
  • 结构化数据:如数据库等。

实现UserDataAbility

UserDataAbility用于接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。

实现UserDataAbility,需要在“Project”窗口当前工程的主目录(“entry > src > main > java > com.xxx.xxx”)选择“File > New > Ability > Empty Data Ability”,设置“Data Name”后完成UserDataAbility的创建。

Data提供了文件存储和数据库存储两组接口供用户使用。

文件存储

开发者需要在Data中重写FileDescriptor openFile​(Uri uri, String mode)方法来操作文件:uri为客户端传入的请求目标路径;mode为开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等。

ohos.rpc.MessageParcel类提供了一个静态方法,用于获取MessageParcel实例。开发者可通过获取到的MessageParcel实例,使用dupFileDescriptor()函数复制待操作文件流的文件描述符,并将其返回,供远端应用访问文件。

示例:根据传入的uri打开对应的文件

private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");

@Override
public FileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    // 创建messageParcel
    MessageParcel messageParcel = MessageParcel.obtain();
    File file = new File(uri.getDecodedPathList().get(0)); //get(0)是获取URI完整字段中查询参数字段。
    if (mode == null || !"rw".equals(mode)) {
        file.setReadOnly();
    }
    FileInputStream fileIs = new FileInputStream(file);
    FileDescriptor fd = null;
    try {
        fd = fileIs.getFD();
    } catch (IOException e) {
        HiLog.info(LABEL_LOG, "failed to getFD");
    }

    // 绑定文件描述符
    return messageParcel.dupFileDescriptor(fd);
}

数据库存储
1、初始化数据库连接。
系统会在应用启动时调用onStart()方法创建Data实例。在此方法中,开发者应该创建数据库连接,并获取连接对象,以便后续和数据库进行操作。为了避免影响应用启动速度,开发者应当尽可能将非必要的耗时任务推迟到使用时执行,而不是在此方法中执行所有初始化。

示例:初始化的时候连接数据库

private static final String DATABASE_NAME = "UserDataAbility.db";
private static final String DATABASE_NAME_ALIAS = "UserDataAbility";
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");
private OrmContext ormContext = null;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    DatabaseHelper manager = new DatabaseHelper(this);
    ormContext = manager.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);
}

2、编写数据库操作方法。
Ability定义了6个方法供用户处理对数据库表数据的增删改查。这6个方法在Ability中已默认实现,开发者可按需重写。

方法 描述
ResultSet query​(Uri uri, String[] columns, DataAbilityPredicates predicates) 查询数据库
int insert​(Uri uri, ValuesBucket value) 向数据库中插入单条数据
int batchInsert​(Uri uri, ValuesBucket[] values) 向数据库中插入多条数据
int delete​(Uri uri, DataAbilityPredicates predicates) 删除一条或多条数据
int update​(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) 更新数据库
DataAbilityResult[] executeBatch​(ArrayList<DataAbilityOperation> operations) 批量操作数据库

3、批量操作数据库
这些方法的使用说明如下:

  • query()
    该方法接收三个参数,分别是查询的目标路径,查询的列名,以及查询条件,查询条件由类DataAbilityPredicates构建。根据传入的列名和查询条件查询用户表的代码示例如下:
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
    if (ormContext == null) {
        HiLog.error(LABEL_LOG, "failed to query, ormContext is null");
        return null;
    }

    // 查询数据库
    OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
    ResultSet resultSet = ormContext.query(ormPredicates, columns);
    if (resultSet == null) {
        HiLog.info(LABEL_LOG, "resultSet is null");
    }

    // 返回结果
    return resultSet;
}
  • insert()
    该方法接收两个参数,分别是插入的目标路径和插入的数据值。其中,插入的数据由ValuesBucket封装,服务端可以从该参数中解析出对应的属性,然后插入到数据库中。此方法返回一个int类型的值用于标识结果。接收到传过来的用户信息并把它保存到数据库中的代码示例如下:
public int insert(Uri uri, ValuesBucket value) {
    // 参数校验
    if (ormContext == null) {
        HiLog.error(LABEL_LOG, "failed to insert, ormContext is null");
        return -1;
    }

    // 构造插入数据
    User user = new User();
    user.setUserId(value.getInteger("userId"));
    user.setFirstName(value.getString("firstName"));
    user.setLastName(value.getString("lastName"));
    user.setAge(value.getInteger("age"));
    user.setBalance(value.getDouble("balance"));

    // 插入数据库
    boolean isSuccessful = ormContext.insert(user);
    if (!isSuccessful) {
        HiLog.error(LABEL_LOG, "failed to insert");
        return -1;
    }
    isSuccessful = ormContext.flush();
    if (!isSuccessful) {
        HiLog.error(LABEL_LOG, "failed to insert flush");
        return -1;
    }
    DataAbilityHelper.creator(this, uri).notifyChange(uri);
    int id = Math.toIntExact(user.getRowId());
    return id;
}
  • batchInsert()
    该方法为批量插入方法,接收一个ValuesBucket数组用于单次插入一组对象。它的作用是提高插入多条重复数据的效率。该方法系统已实现,开发者可以直接调用。
  • delete()
    该方法用来执行删除操作。删除条件由类DataAbilityPredicates构建,服务端在接收到该参数之后可以从中解析出要删除的数据,然后到数据库中执行。根据传入的条件删除用户表数据的代码示例如下:
public int delete(Uri uri, DataAbilityPredicates predicates) {
    if (ormContext == null) {
        HiLog.error(LABEL_LOG, "failed to delete, ormContext is null");
        return -1;
    }

    OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
    int value = ormContext.delete(ormPredicates);
    DataAbilityHelper.creator(this, uri).notifyChange(uri);
    return value;
}
  • update()
    此方法用来执行更新操作。用户可以在ValuesBucket参数中指定要更新的数据,在DataAbilityPredicates中构建更新的条件等。更新用户表的数据的代码示例如下:
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
    if (ormContext == null) {
       HiLog.error(LABEL_LOG, "failed to update, ormContext is null");
       return -1;
   }

   OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
   int index = ormContext.update(ormPredicates, value);
   HiLog.info(LABEL_LOG, "UserDataAbility update value:" + index);
   DataAbilityHelper.creator(this, uri).notifyChange(uri);
   return index;
}
  • executeBatch()
    此方法用来批量执行操作。DataAbilityOperation中提供了设置操作类型、数据和操作条件的方法,用户可自行设置自己要执行的数据库操作。该方法系统已实现,开发者可以直接调用。

说明
上述代码示例中,初始化了数据库类BookStore.class,并通过实体类User.class对该数据库的表User进行增删改查操作。
关于对象关系映射数据库的具体逻辑,以及示例中BookStore.class与User.class的逻辑关系,可参考“对象关系映射数据库开发指导”。

注册UserDataAbility

和Service类似,开发者必须在配置文件中注册Data。
配置文件中该字段在创建Data Ability时会自动创建,name与创建的Data Ability一致。
需要关注以下属性:

  • type: 类型设置为data
  • uri: 对外提供的访问路径,全局唯一
  • permissions: 访问该data ability时需要申请的访问权限
    说明
    如果权限非系统权限,需要在配置文件中进行自定义。请参考权限开发指导中关于“自定义权限”的相关说明。
{
    "name": ".UserDataAbility",
     "type": "data",
     "visible": true,
     "uri": "dataability://com.example.myapplication5.DataAbilityTest",
     "permissions": [
        "com.example.myapplication5.DataAbility.DATA"
     ]
}

访问Data

开发者可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。DataAbilityHelper作为客户端,与提供方的Data进行通信。Data接收到请求后,执行相应的处理,并返回结果。DataAbilityHelper提供了一系列与Data Ability对应的方法。

下面介绍DataAbilityHelper具体的使用步骤。

声明使用权限

如果待访问的Data声明了访问需要权限,则访问此Data需要在配置文件中声明需要此权限。声明请参考权限申请字段说明。

"reqPermissions": [
    {
        "name": "com.example.myapplication5.DataAbility.DATA"
    },
    // 访问文件还需要添加访问存储读写权限
    {
        "name": "ohos.permission.READ_USER_STORAGE"
    },
    {
        "name": "ohos.permission.WRITE_USER_STORAGE"
    }
]

创建DataAbilityHelper

DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例。该方法为静态方法,有多个重载。最常见的方法是通过传入一个context对象来创建DataAbilityHelper对象。

获取helper对象示例:

DataAbilityHelper helper = DataAbilityHelper.creator(this);

访问Data Ability

DataAbilityHelper为开发者提供了一系列的接口来访问不同类型的数据(文件、数据库等)。

  • 访问文件
    DataAbilityHelper为开发者提供了FileDescriptor openFile​(Uri uri, String mode)方法来操作文件。此方法需要传入两个参数,其中uri用来确定目标资源路径,mode用来指定打开文件的方式,可选方式包含“r”(读), “w”(写), “rw”(读写),“wt”(覆盖写),“wa”(追加写),“rwt”(覆盖写且可读)。
    该方法返回一个目标文件的FD(文件描述符),把文件描述符封装成流,开发者就可以对文件流进行自定义处理。
    访问文件示例:
// 读取文件描述符
FileDescriptor fd = helper.openFile(uri, "r");
FileInputStream fis = new FileInputStream(fd);
// 使用文件描述符封装成的文件流,进行文件操作
  • 访问数据库
    DataAbilityHelper为开发者提供了增、删、改、查以及批量处理等方法来操作数据库。
    说明
    对数据库的操作方法,详见数据管理中各数据库类型的开发指南。
方法 描述
ResultSet query​(Uri uri, String[] columns, DataAbilityPredicates predicates) 查询数据库
int insert​(Uri uri, ValuesBucket value) 向数据库中插入单条数据
int batchInsert​(Uri uri, ValuesBucket[] values) 向数据库中插入多条数据
int delete​(Uri uri, DataAbilityPredicates predicates) 删除一条或多条数据
int update​(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) 更新数据库
DataAbilityResult[] executeBatch​(ArrayList<DataAbilityOperation> operations) 批量操作数据库

这些方法的使用说明如下:

  • query()
    查询方法,其中uri为目标资源路径,columns为想要查询的字段。开发者的查询条件可以通过DataAbilityPredicates来构建。查询用户表中id在101-103之间的用户,并把结果打印出来,代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造查询条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
// 进行查询
ResultSet resultSet = helper.query(uri, columns, predicates);
// 处理结果
resultSet.goToFirstRow();
do {
    // 在此处理ResultSet中的记录;
} while(resultSet.goToNextRow());
  • insert()
    新增方法,其中uri为目标资源路径,ValuesBucket为要新增的对象。插入一条用户信息的代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造插入数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.insert(uri, valuesBucket);
  • batchInsert()
    批量插入方法,和insert()类似。批量插入用户信息的代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造插入数据
ValuesBucket[] values = new ValuesBucket[2];
values[0] = new ValuesBucket();
values[0].putString("name", "Tom");
values[0].putInteger("age", 12);
values[1] = new ValuesBucket();
values[1].putString("name", "Tom1");
values[1].putInteger("age", 16);
helper.batchInsert(uri, values);
  • delete()
    删除方法,其中删除条件可以通过DataAbilityPredicates来构建。删除用户表中id在101-103之间的用户,代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造删除条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
helper.delete(uri, predicates);
  • update()
    更新方法,更新数据由ValuesBucket传入,更新条件由DataAbilityPredicates来构建。更新id为102的用户,代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造更新条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo("userId", 102);
// 构造更新数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.update(uri, valuesBucket, predicates);
  • executeBatch()
    此方法用来执行批量操作。DataAbilityOperation中提供了设置操作类型、数据和操作条件的方法,开发者可自行设置自己要执行的数据库操作。插入多条数据的代码示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(abilityObj, insertUri);
// 构造批量操作
ValuesBucket value1 = initSingleValue();
DataAbilityOperation opt1 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value1).build();
ValuesBucket value2 = initSingleValue2();
DataAbilityOperation opt2 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value2).build();
ArrayList<DataAbilityOperation> operations = new ArrayList<DataAbilityOperation>();
operations.add(opt1);
operations.add(opt2);
DataAbilityResult[] result = helper.executeBatch(insertUri, operations);

相关实例

针对Data Ability开发,有以下示例工程可供参考:

  • DataAbility
    本示例演示了如何使用Data Ability对数据库进行增、删、改、查,以及读取文本文件。
    针对Data Ability开发,有以下Codelabs可供参考:

  • 关系型数据库
    基于Data Ability的关系型数据库和数据管理能力,实现数据库相关应用服务的快速开发。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
3
收藏 5
回复
举报
2条回复
按时间正序
/
按时间倒序
wx5cb5e95304723
wx5cb5e95304723

请问如何写入文件呢。

回复
2022-1-16 16:42:45
爱吃土豆丝的打工人
爱吃土豆丝的打工人 回复了 wx5cb5e95304723
请问如何写入文件呢。

FileDescriptor openFile​(Uri uri, String mode)方法来操作文件:uri为客户端传入的请求目标路径;mode为开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等。

1
回复
2022-1-17 14:35:45
回复
    相关推荐