HarmonyOS Sample之DataAbility RDB數據庫操作
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