写点什么

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

发布于: 22 小时前
HarmonyOS学习路之开发篇——Data Ability

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");
@Overridepublic 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;
@Overridepublic 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 中已默认实现,开发者可按需重写。



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 为开发者提供了增、删、改、查以及批量处理等方法来操作数据库。说明对数据库的操作方法,详见数据管理中各数据库类型的开发指南。



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


  • 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 的关系型数据库和数据管理能力,实现数据库相关应用服务的快速开发。

发布于: 22 小时前阅读数: 4
用户头像

公众号【美男子玩编程】 2020.05.07 加入

精通移动开发、Android开发; 熟练应用java/JavaScript进行HarmonyOS开发; 熟练使用HTML/CSS语言进行网页开发。

评论

发布
暂无评论
HarmonyOS学习路之开发篇——Data Ability