HarmonyOS Sample之DataAbility RDB数据库操作
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
DataAbility RDB数据库操作
介绍
使用Data模板的Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
本示例演示了如何使用Data Ability对RDB数据库进行增、删、改、查,以及读取文本文件。
模仿手机的备忘录,实现了简单的操作。
搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
步骤
1.创建一个DataAbility和数据库常量类
a.创建一个Empty DataAbility
entity右键,New- Ability-Empty Data Ability,然后输入名称 NoteDataAbility

b.创建一个数据库常量类 Const.java
存放数据库名称、表名称、字段列名称、存储路径等
需要注意的是,
BASE_URI 3个杠后面的部分要和config.json Data Ability 声明的uri完全一致,否则应用无法启动
/** * Const */ public class Const { /** * DataAbility base uri * scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。 * authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。 * path:资源的路径信息,代表特定资源的位置信息。 * query:查询参数。 * fragment:可以用于指示要访问的子资源。 * 本地设备的“device_id”字段为空,因此在“dataability:”后面有三个“/” * * BASE_URI 3个杠后面的部分要和config.json Data Ability 声明的uri完全一致,否则应用无法启动 * */ public static final String BASE_URI = "dataability:///ohos.samples.dataability.NoteDataAbility"; /** * Database name */ public static final String DB_NAME = "note.db"; /** * Database table name */ public static final String DB_TAB_NAME = "note"; /** * Database column name:Id */ public static final String DB_COLUMN_ID = "Id"; /** * Database column name:noteTitle */ public static final String DB_COLUMN_TITLE = "noteTitle"; /** * Database column name:writeTime */ public static final String DB_COLUMN_TIME = "writeTime"; /** * Database column name:noteCategory */ public static final String DB_COLUMN_CATEGORY = "noteCategory"; /** * Database column name:noteContent */ public static final String DB_COLUMN_CONTENT = "noteContent"; /** * Database data path */ public static final String DATA_PATH = "/note"; /** * 文件名称 */ public static final String FILE_NAME = "userdataability.txt"; }c.config.json相关配置
config.json涉及NoteDataAbility.java 的地方有3处,
第1处在module对象下,

第2处是abilities对象下,
permissions表示其他应用的能力调用当前能力所需的权限。
默认情况下隐藏"visible"字段(值为false),表示仅本应用可访问该Data,开发人员可根据需求修改permissions、visible值、uri等内容。当外部应用需要访问/控制此数据库字段时,在该Data Ability配置中增加"visible": true,并在外面应用的配置文件config.json中申请permissions权限。

第3处是reqPermissions对象下,
说明:如果待访问的Data Ability是由本应用创建,则可以不声明该权限。

2.声明数据库存储对象和数据库配置
在NoteDataAbility.java 添加如下代码
//声明数据库存储对象 private RdbStore rdbStore; //数据库配置,指定数据库名称 private StoreConfig storeConfig = StoreConfig.newDefaultConfig(Const.DB_NAME);3.实现打开RDB数据库回调函数
在NoteDataAbility.java 添加如下代码
// 管理数据库创建、升级和降级。 // 您可以创建一个子类来实现 #onCreate、#onUpgrade 或 #onOpen 方法。 // 如果一个数据库已经存在,它将被打开; 如果不存在数据库,则将创建一个数据库。 // 在数据库升级过程中,也会调用该类的方法。 private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() { @Override public void onCreate(RdbStore rdbStore) { //创建表 rdbStore.executeSql( "create table if not exists " + Const.DB_TAB_NAME + "2 (" + Const.DB_COLUMN_ID + " integer primary key autoincrement ," + Const.DB_COLUMN_TITLE + " text not null," + Const.DB_COLUMN_CONTENT + " text not null," + Const.DB_COLUMN_TIME + " text not null," + Const.DB_COLUMN_CATEGORY + " text not null" + ")" ); } @Override public void onUpgrade(RdbStore rdbStore, int i, int i1) { //数据库升级 } };4.初始化RDB数据库存储对象
在NoteDataAbility.java 添加如下代码
@Override public void onStart(Intent intent) { super.onStart(intent); HiLog.info(LABEL_LOG, "NoteDataAbility onStart"); //数据库帮助类 DatabaseHelper databaseHelper = new DatabaseHelper(this); //初始化RDB数据库存储对象 rdbStore = databaseHelper.getRdbStore(storeConfig, 1, rdbOpenCallback); }5.实现对数据库的基本操作函数
NoteDataAbility.java操作数据库的方法都需要自己实现,包括:添加、修改、查询、删除,还有打开文件,主要使用rdbStore对象。
@Override public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) { HiLog.info(LABEL_LOG, "NoteDataAbility query"); RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME); return rdbStore.query(rdbPredicates, columns); } @Override public int insert(Uri uri, ValuesBucket value) { HiLog.info(LABEL_LOG, "NoteDataAbility insert"); //long to int int rowId = (int) rdbStore.insert(Const.DB_TAB_NAME, value); //通知观察者数据发生变化 DataAbilityHelper.creator(this).notifyChange(uri); return rowId; } @Override public int delete(Uri uri, DataAbilityPredicates predicates) { //rdb 条件,通过DataAbilityUtils将DataAbilityPredicates转成 RdbPredicates RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME); //执行删除 int rowId = rdbStore.delete(rdbPredicates); HiLog.info(LABEL_LOG, "%{public}s", "delete"); //通知观察者数据发生变化 DataAbilityHelper.creator(this).notifyChange(uri); return rowId; } @Override public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) { //rdb 条件,通过DataAbilityUtils将DataAbilityPredicates转成 RdbPredicates RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME); int rowId =rdbStore.update(value, rdbPredicates); //通知观察者数据发生变化 DataAbilityHelper.creator(this).notifyChange(uri); return rowId; } @Override public FileDescriptor openFile(Uri uri, String mode) { //获取应用程序在设备内部存储器上存放文件的目录 File file = new File(getFilesDir(), uri.getDecodedQuery()); FileDescriptor fileDescriptor = null; try { FileInputStream fis = new FileInputStream(file); //获取FD fileDescriptor = fis.getFD(); //获取一个新的文件描述符,它是现有文件描述符的副本 return MessageParcel.dupFileDescriptor(fileDescriptor); } catch (IOException e) { e.printStackTrace(); } return fileDescriptor; }6.数据的订阅和通知
在NoteDataAbility.java 中, 我们看到insert/update/delete方法都有一行。
DataAbilityHelper.creator(this).notifyChange(uri);目的是在数据库数据发生变化时,通知数据的订阅者。
而在MainAbilitySlice.java 类中有如下方法,在OnStart()中被调用,实现了数据变化的订阅。
private void initDatabaseHelper() { //创建实例 dataAbilityHelper = DataAbilityHelper.creator(this); //注册一个观察者来观察给定 Uri 指定的数据,dataObserver表示 IDataAbilityObserver 对象 dataAbilityHelper.registerObserver(Uri.parse(Const.BASE_URI), dataAbilityObserver); }同时,数据变化订阅方还需要实现IDataAbilityObserver接口,在数据变化时会自动回调,完成对应的逻辑处理。
//观察者模式,数据变化时回调 private final IDataAbilityObserver dataAbilityObserver=() -> { HiLog.info(LABEL, "%{public}s", "database changed"); //筛选数据 initLists(this); };当数据订阅者不再需要订阅Data变化时,则调用unregisterObserver(Uri uri, IDataAbilityObserver dataObserver)方法取消。
@Override protected void onStop() { super.onStop(); dataAbilityHelper.unregisterObserver(Uri.parse(Const.BASE_URI), dataAbilityObserver); }观察者模式的作用在于当数据库表格的内容产生变化时,可以主动通知与该表格数据相关联的进程或者应用,从而使得相关进程或者应用接收到数据变化后完成相应的处理。
7.访问Data Ability,新建AddNoteAbility,在AddNoteAbilitySlice实现数据的添加和修改
开发者可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。
DataAbilityHelper作为客户端,与提供方的Data进行通信。DataAbilityHelper提供了一系列与Data Ability通信的方法。
a.数据的添加
/** * 保存数据 * * @param component component */ private void saveNote(Component component) { ValuesBucket valuesBucket = new ValuesBucket(); TextField noteTitle = (TextField) findComponentById(ResourceTable.Id_add_note_title); if (noteTitle.getText().isEmpty()) { DialLogUtils dialog = new DialLogUtils(this, "标题不能为空!"); dialog.showDialog(); return; } TextField noteContent = (TextField) findComponentById(ResourceTable.Id_add_note_content); if (noteContent.getText().isEmpty()) { DialLogUtils dialog = new DialLogUtils(this, "内容不能为空!"); dialog.showDialog(); return; } Text noteCategory = (Text) findComponentById(ResourceTable.Id_add_note_category); Text noteTime = (Text) findComponentById(ResourceTable.Id_add_note_time); HiLog.debug(LABEL, "%{public}s", "saveNote, noteId:[" + noteId + "],noteCategory:" + noteCategory.getText()); int rowId; //放入键值 valuesBucket.putString(Const.DB_COLUMN_TITLE, noteTitle.getText()); valuesBucket.putString(Const.DB_COLUMN_CATEGORY, noteCategory.getText()); valuesBucket.putString(Const.DB_COLUMN_CONTENT, noteContent.getText()); valuesBucket.putString(Const.DB_COLUMN_TIME, noteTime.getText()); try { if (noteId.isEmpty()) { HiLog.debug(LABEL, "%{public}s", "saveNote, insert"); //插入数据 rowId = dataAbilityHelper.insert(Uri.parse(Const.BASE_URI + Const.DATA_PATH), valuesBucket); HiLog.debug(LABEL, "%{public}s", "insert,rowId:" + rowId); } else { HiLog.debug(LABEL, "%{public}s", "saveNote, update"); //指定修改谓语 DataAbilityPredicates predicates = new DataAbilityPredicates(); predicates.equalTo(Const.DB_COLUMN_ID, noteId); //修改数据 rowId = dataAbilityHelper.update(Uri.parse(Const.BASE_URI + Const.DATA_PATH), valuesBucket, predicates); HiLog.debug(LABEL, "%{public}s", "update,rowId:" + rowId); } //返回列表页 backListPage(); } catch (DataAbilityRemoteException | IllegalStateException exception) { HiLog.error(LABEL, "%{public}s", "insert: dataRemote exception|illegalStateException"); } }b.修改和删除数据
@Override public void onStart(Intent intent) { super.onStart(intent); //设置UI布局资源 super.setUIContent(ResourceTable.Layout_ability_add_note); // initDatabaseHelper(); //返回按钮 Component backButton = findComponentById(ResourceTable.Id_back_image); backButton.setClickedListener(component -> terminateAbility()); TextField noteContent = (TextField) findComponentById(ResourceTable.Id_add_note_content); //修改笔记 if (intent.hasParameter("Id")) { HiLog.info(LABEL, "%{public}s", "change data coming"); noteId = intent.getStringParam("Id"); HiLog.info(LABEL, "%{public}s", "noteId:" + noteId); if (noteId != null) { DataAbilityPredicates predicates = new DataAbilityPredicates(); predicates.equalTo(Const.DB_COLUMN_ID, noteId); //查询数据 NoteListItemInfo itemInfo = queryOne(predicates); HiLog.info(LABEL, "%{public}s", "noteTitle:" + itemInfo.getNoteTitle() + ",category:" + itemInfo.getNoteCategory()); //设置显示 TextField noteTitle = (TextField) findComponentById(ResourceTable.Id_add_note_title); noteTitle.setText(itemInfo.getNoteTitle()); noteContent.setText(itemInfo.getNoteContent()); Text category = (Text) findComponentById(ResourceTable.Id_add_note_category); category.setText(itemInfo.getNoteCategory()); Text noteTime = (Text) findComponentById(ResourceTable.Id_add_note_time); noteTime.setText(itemInfo.getNoteTime()); Component deleteButton = findComponentById(ResourceTable.Id_delete_image); //设置删除按钮可用,只有修改笔记才能删除 deleteButton.setClickable(true); //添加事件 deleteButton.setClickedListener(component -> { try { int rowId = dataAbilityHelper.delete(Uri.parse(Const.BASE_URI + Const.DATA_PATH), predicates); HiLog.info(LABEL, "%{public}s", "deleteNote,rowId:" + rowId); //返回列表页 backListPage(); } catch (DataAbilityRemoteException e) { HiLog.error(LABEL, "%{public}s", "delete: exception|DataAbilityRemoteException"); } }); } } else { Text timeText = (Text) findComponentById(ResourceTable.Id_add_note_time); String time24 = sdf.format(new Date()); timeText.setText(time24); } //保存笔记 Component insertButton = findComponentById(ResourceTable.Id_finish_image); insertButton.setClickedListener(this::saveNote); }c.查询数据
private NoteListItemInfo queryOne(DataAbilityPredicates predicates) { HiLog.info(LABEL, "%{public}s", "database query"); String[] columns = new String[]{ Const.DB_COLUMN_ID, Const.DB_COLUMN_TITLE, Const.DB_COLUMN_TIME, Const.DB_COLUMN_CATEGORY, Const.DB_COLUMN_CONTENT}; try { ResultSet resultSet = dataAbilityHelper.query( Uri.parse(Const.BASE_URI + Const.DATA_PATH), columns, predicates); //无数据 if (resultSet.getRowCount() == 0) { HiLog.info(LABEL, "%{public}s", "query:No result found"); return null; } // resultSet.goToFirstRow(); //根据列索引获取列值 String noteId = resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_ID)); String noteTitle = resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_TITLE)); String noteTime = resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_TIME)); String noteCategory = resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_CATEGORY)); String noteContent = resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_CONTENT)); Element image = ElementScatter.getInstance(getContext()).parse(ResourceTable.Graphic_icon_nodata); HiLog.info(LABEL, "%{public}s", "set show:" + noteCategory); // return new NoteListItemInfo(noteId, noteTitle, noteContent, noteTime, noteCategory, image); } catch (DataAbilityRemoteException | IllegalStateException exception) { HiLog.error(LABEL, "%{public}s", "query: dataRemote exception|illegalStateException"); } return null; }实践中遇到的小知识点记录一下
1. 如何监听 TextField 文本变更事件
/** * 监听TextFiled 文本变化 */ private void initSearchBtnEvent(AbilitySlice slice) { TextField searchTF = (TextField) findComponentById(ResourceTable.Id_tf_note_search); //添加文本观察器 TextObserver 以检测文本是否发生更改。 searchTF.addTextObserver(new Text.TextObserver() { @Override public void onTextUpdated(String s, int i, int i1, int i2) { HiLog.info(LABEL, "addTextObserver 按键事件触发....."); //筛选数据 initLists(slice); } }); }2. ListContainer 组件添加点击事件
在 Provider 中 getComponent添加,在初始化Provider时传递AbilitySlice对象过来
public ListItemProvider(List<ItemInfo> itemList, AbilityContext context,AbilitySlice slice) { this.itemList = itemList; this.context = context; this.typeFactory = new ListTypeFactory(); this.slice=slice; } @Override public Component getComponent(int index, Component component, ComponentContainer componentContainer) { Component itemComponent = component; ViewHolder viewHolder; if (itemComponent == null) { itemComponent = LayoutScatter.getInstance(componentContainer.getContext()) .parse(getItemComponentType(index), componentContainer, false); } viewHolder = typeFactory.getViewHolder(getItemComponentType(index), itemComponent); viewHolder.setUpComponent(getItem(index), context); //设置点击事件 itemComponent.setClickedListener(component1 -> { //获取noteId String noteId=""; if(getItem(index) instanceof NoteListItemInfo){ //HiLog.debug(LABEL, "%{public}s", "ItemInfo instanceof SingleButtonDoubleLineListItemInfo"); noteId=((NoteListItemInfo)getItem(index)).getNoteId(); } HiLog.debug(LABEL, "%{public}s", "noteId:" + noteId); //1.携带笔记ID参数,跳转到AddNoteAbilitySlice Intent intent = new Intent(); if(noteId!=null){ //保存要传递的参数 intent.setParam("Id", noteId); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.buty.samples") .withAbilityName(AddNoteAbility.class).build(); intent.setOperation(operation); slice.startAbility(intent); }else { HiLog.error(LABEL, "%{public}s", "noteId is null"); } }); return itemComponent; }效果展示
文章相关附件可以点击下面的原文链接前往下载。
原文链接:https://harmonyos.51cto.com/posts/7386
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
扫一扫,关注我们