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