HarmonyOS Sample之DataAbility RDB數(shù)據(jù)庫操作
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
DataAbility RDB數(shù)據(jù)庫操作
介紹
使用Data模板的Ability(以下簡稱“Data”)有助于應(yīng)用管理其自身和其他應(yīng)用存儲(chǔ)數(shù)據(jù)的訪問,并提供與其他應(yīng)用共享數(shù)據(jù)的方法。Data既可用于同設(shè)備不同應(yīng)用的數(shù)據(jù)共享,也支持跨設(shè)備不同應(yīng)用的數(shù)據(jù)共享。
數(shù)據(jù)的存放形式多樣,可以是數(shù)據(jù)庫,也可以是磁盤上的文件。Data對外提供對數(shù)據(jù)的增、刪、改、查,以及打開文件等接口,這些接口的具體實(shí)現(xiàn)由開發(fā)者提供。
本示例演示了如何使用Data Ability對RDB數(shù)據(jù)庫進(jìn)行增、刪、改、查,以及讀取文本文件。
模仿手機(jī)的備忘錄,實(shí)現(xiàn)了簡單的操作。
搭建環(huán)境
安裝DevEco Studio,詳情請參考DevEco Studio下載。
設(shè)置DevEco Studio開發(fā)環(huán)境,DevEco Studio開發(fā)環(huán)境需要依賴于網(wǎng)絡(luò)環(huán)境,需要連接上網(wǎng)絡(luò)才能確保工具的正常使用,可以根據(jù)如下兩種情況來配置開發(fā)環(huán)境:
如果可以直接訪問Internet,只需進(jìn)行下載HarmonyOS SDK操作。
如果網(wǎng)絡(luò)不能直接訪問Internet,需要通過代理服務(wù)器才可以訪問,請參考配置開發(fā)環(huán)境。
步驟
1.創(chuàng)建一個(gè)DataAbility和數(shù)據(jù)庫常量類
a.創(chuàng)建一個(gè)Empty DataAbility
entity右鍵,New- Ability-Empty Data Ability,然后輸入名稱 NoteDataAbility

b.創(chuàng)建一個(gè)數(shù)據(jù)庫常量類 Const.java
存放數(shù)據(jù)庫名稱、表名稱、字段列名稱、存儲(chǔ)路徑等
需要注意的是,
BASE_URI 3個(gè)杠后面的部分要和config.json Data Ability 聲明的uri完全一致,否則應(yīng)用無法啟動(dòng)
- /**
- * Const
- */
- public class Const {
- /**
- * DataAbility base uri
- * scheme:協(xié)議方案名,固定為“dataability”,代表Data Ability所使用的協(xié)議類型。
- * authority:設(shè)備ID。如果為跨設(shè)備場景,則為目標(biāo)設(shè)備的ID;如果為本地設(shè)備場景,則不需要填寫。
- * path:資源的路徑信息,代表特定資源的位置信息。
- * query:查詢參數(shù)。
- * fragment:可以用于指示要訪問的子資源。
- * 本地設(shè)備的“device_id”字段為空,因此在“dataability:”后面有三個(gè)“/”
- *
- * BASE_URI 3個(gè)杠后面的部分要和config.json Data Ability 聲明的uri完全一致,否則應(yīng)用無法啟動(dòng)
- *
- */
- 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相關(guān)配置
config.json涉及NoteDataAbility.java 的地方有3處,
第1處在module對象下,

第2處是abilities對象下,
permissions表示其他應(yīng)用的能力調(diào)用當(dāng)前能力所需的權(quán)限。
默認(rèn)情況下隱藏"visible"字段(值為false),表示僅本應(yīng)用可訪問該Data,開發(fā)人員可根據(jù)需求修改permissions、visible值、uri等內(nèi)容。當(dāng)外部應(yīng)用需要訪問/控制此數(shù)據(jù)庫字段時(shí),在該Data Ability配置中增加"visible": true,并在外面應(yīng)用的配置文件config.json中申請permissions權(quán)限。

第3處是reqPermissions對象下,
說明:如果待訪問的Data Ability是由本應(yīng)用創(chuàng)建,則可以不聲明該權(quán)限。

2.聲明數(shù)據(jù)庫存儲(chǔ)對象和數(shù)據(jù)庫配置
在NoteDataAbility.java 添加如下代碼
- //聲明數(shù)據(jù)庫存儲(chǔ)對象
- private RdbStore rdbStore;
- //數(shù)據(jù)庫配置,指定數(shù)據(jù)庫名稱
- private StoreConfig storeConfig = StoreConfig.newDefaultConfig(Const.DB_NAME);
3.實(shí)現(xiàn)打開RDB數(shù)據(jù)庫回調(diào)函數(shù)
在NoteDataAbility.java 添加如下代碼
- // 管理數(shù)據(jù)庫創(chuàng)建、升級和降級。
- // 您可以創(chuàng)建一個(gè)子類來實(shí)現(xiàn) #onCreate、#onUpgrade 或 #onOpen 方法。
- // 如果一個(gè)數(shù)據(jù)庫已經(jīng)存在,它將被打開; 如果不存在數(shù)據(jù)庫,則將創(chuàng)建一個(gè)數(shù)據(jù)庫。
- // 在數(shù)據(jù)庫升級過程中,也會(huì)調(diào)用該類的方法。
- private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() {
- @Override
- public void onCreate(RdbStore rdbStore) {
- //創(chuàng)建表
- 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) {
- //數(shù)據(jù)庫升級
- }
- };
4.初始化RDB數(shù)據(jù)庫存儲(chǔ)對象
在NoteDataAbility.java 添加如下代碼
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- HiLog.info(LABEL_LOG, "NoteDataAbility onStart");
- //數(shù)據(jù)庫幫助類
- DatabaseHelper databaseHelper = new DatabaseHelper(this);
- //初始化RDB數(shù)據(jù)庫存儲(chǔ)對象
- rdbStore = databaseHelper.getRdbStore(storeConfig, 1, rdbOpenCallback);
- }
5.實(shí)現(xiàn)對數(shù)據(jù)庫的基本操作函數(shù)
NoteDataAbility.java操作數(shù)據(jù)庫的方法都需要自己實(shí)現(xiàn),包括:添加、修改、查詢、刪除,還有打開文件,主要使用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);
- //通知觀察者數(shù)據(jù)發(fā)生變化
- DataAbilityHelper.creator(this).notifyChange(uri);
- return rowId;
- }
- @Override
- public int delete(Uri uri, DataAbilityPredicates predicates) {
- //rdb 條件,通過DataAbilityUtils將DataAbilityPredicates轉(zhuǎn)成 RdbPredicates
- RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME);
- //執(zhí)行刪除
- int rowId = rdbStore.delete(rdbPredicates);
- HiLog.info(LABEL_LOG, "%{public}s", "delete");
- //通知觀察者數(shù)據(jù)發(fā)生變化
- DataAbilityHelper.creator(this).notifyChange(uri);
- return rowId;
- }
- @Override
- public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
- //rdb 條件,通過DataAbilityUtils將DataAbilityPredicates轉(zhuǎn)成 RdbPredicates
- RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME);
- int rowId =rdbStore.update(value, rdbPredicates);
- //通知觀察者數(shù)據(jù)發(fā)生變化
- DataAbilityHelper.creator(this).notifyChange(uri);
- return rowId;
- }
- @Override
- public FileDescriptor openFile(Uri uri, String mode) {
- //獲取應(yīng)用程序在設(shè)備內(nèi)部存儲(chǔ)器上存放文件的目錄
- File file = new File(getFilesDir(), uri.getDecodedQuery());
- FileDescriptor fileDescriptor = null;
- try {
- FileInputStream fis = new FileInputStream(file);
- //獲取FD
- fileDescriptor = fis.getFD();
- //獲取一個(gè)新的文件描述符,它是現(xiàn)有文件描述符的副本
- return MessageParcel.dupFileDescriptor(fileDescriptor);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return fileDescriptor;
- }
6.數(shù)據(jù)的訂閱和通知
在NoteDataAbility.java 中, 我們看到insert/update/delete方法都有一行。
- DataAbilityHelper.creator(this).notifyChange(uri);
目的是在數(shù)據(jù)庫數(shù)據(jù)發(fā)生變化時(shí),通知數(shù)據(jù)的訂閱者。
而在MainAbilitySlice.java 類中有如下方法,在OnStart()中被調(diào)用,實(shí)現(xiàn)了數(shù)據(jù)變化的訂閱。
- private void initDatabaseHelper() {
- //創(chuàng)建實(shí)例
- dataAbilityHelper = DataAbilityHelper.creator(this);
- //注冊一個(gè)觀察者來觀察給定 Uri 指定的數(shù)據(jù),dataObserver表示 IDataAbilityObserver 對象
- dataAbilityHelper.registerObserver(Uri.parse(Const.BASE_URI), dataAbilityObserver);
- }
同時(shí),數(shù)據(jù)變化訂閱方還需要實(shí)現(xiàn)IDataAbilityObserver接口,在數(shù)據(jù)變化時(shí)會(huì)自動(dòng)回調(diào),完成對應(yīng)的邏輯處理。
- //觀察者模式,數(shù)據(jù)變化時(shí)回調(diào)
- private final IDataAbilityObserver dataAbilityObserver=() -> {
- HiLog.info(LABEL, "%{public}s", "database changed");
- //篩選數(shù)據(jù)
- initLists(this);
- };
當(dāng)數(shù)據(jù)訂閱者不再需要訂閱Data變化時(shí),則調(diào)用unregisterObserver(Uri uri, IDataAbilityObserver dataObserver)方法取消。
- @Override
- protected void onStop() {
- super.onStop();
- dataAbilityHelper.unregisterObserver(Uri.parse(Const.BASE_URI), dataAbilityObserver);
- }
觀察者模式的作用在于當(dāng)數(shù)據(jù)庫表格的內(nèi)容產(chǎn)生變化時(shí),可以主動(dòng)通知與該表格數(shù)據(jù)相關(guān)聯(lián)的進(jìn)程或者應(yīng)用,從而使得相關(guān)進(jìn)程或者應(yīng)用接收到數(shù)據(jù)變化后完成相應(yīng)的處理。
7.訪問Data Ability,新建AddNoteAbility,在AddNoteAbilitySlice實(shí)現(xiàn)數(shù)據(jù)的添加和修改
開發(fā)者可以通過DataAbilityHelper類來訪問當(dāng)前應(yīng)用或其他應(yīng)用提供的共享數(shù)據(jù)。
DataAbilityHelper作為客戶端,與提供方的Data進(jìn)行通信。DataAbilityHelper提供了一系列與Data Ability通信的方法。
a.數(shù)據(jù)的添加
- /**
- * 保存數(shù)據(jù)
- *
- * @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, "標(biāo)題不能為空!");
- dialog.showDialog();
- return;
- }
- TextField noteContent = (TextField) findComponentById(ResourceTable.Id_add_note_content);
- if (noteContent.getText().isEmpty()) {
- DialLogUtils dialog = new DialLogUtils(this, "內(nèi)容不能為空!");
- 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");
- //插入數(shù)據(jù)
- 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);
- //修改數(shù)據(jù)
- 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.修改和刪除數(shù)據(jù)
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- //設(shè)置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);
- //查詢數(shù)據(jù)
- NoteListItemInfo itemInfo = queryOne(predicates);
- HiLog.info(LABEL, "%{public}s", "noteTitle:" + itemInfo.getNoteTitle() + ",category:" + itemInfo.getNoteCategory());
- //設(shè)置顯示
- 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);
- //設(shè)置刪除按鈕可用,只有修改筆記才能刪除
- 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.查詢數(shù)據(jù)
- 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);
- //無數(shù)據(jù)
- if (resultSet.getRowCount() == 0) {
- HiLog.info(LABEL, "%{public}s", "query:No result found");
- return null;
- }
- //
- resultSet.goToFirstRow();
- //根據(jù)列索引獲取列值
- 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;
- }
實(shí)踐中遇到的小知識(shí)點(diǎn)記錄一下
1. 如何監(jiān)聽 TextField 文本變更事件
- /**
- * 監(jiān)聽TextFiled 文本變化
- */
- private void initSearchBtnEvent(AbilitySlice slice) {
- TextField searchTF = (TextField) findComponentById(ResourceTable.Id_tf_note_search);
- //添加文本觀察器 TextObserver 以檢測文本是否發(fā)生更改。
- searchTF.addTextObserver(new Text.TextObserver() {
- @Override
- public void onTextUpdated(String s, int i, int i1, int i2) {
- HiLog.info(LABEL, "addTextObserver 按鍵事件觸發(fā).....");
- //篩選數(shù)據(jù)
- initLists(slice);
- }
- });
- }
2. ListContainer 組件添加點(diǎn)擊事件
在 Provider 中 getComponent添加,在初始化Provider時(shí)傳遞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);
- //設(shè)置點(diǎn)擊事件
- 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參數(shù),跳轉(zhuǎn)到AddNoteAbilitySlice
- Intent intent = new Intent();
- if(noteId!=null){
- //保存要傳遞的參數(shù)
- 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;
- }
效果展示
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載。
原文鏈接:https://harmonyos.51cto.com/posts/7386
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)