HarmonyOS學(xué)習(xí)路之開(kāi)發(fā)篇—Data Ability
1
Data Ability基本概念
使用Data模板的Ability(以下簡(jiǎn)稱“Data”)有助于應(yīng)用管理其自身和其他應(yīng)用存儲(chǔ)數(shù)據(jù)的訪問(wèn),并提供與其他應(yīng)用共享數(shù)據(jù)的方法。Data既可用于同設(shè)備不同應(yīng)用的數(shù)據(jù)共享,也支持跨設(shè)備不同應(yīng)用的數(shù)據(jù)共享。
數(shù)據(jù)的存放形式多樣,可以是數(shù)據(jù)庫(kù),也可以是磁盤上的文件。Data對(duì)外提供對(duì)數(shù)據(jù)的增、刪、改、查,以及打開(kāi)文件等接口,這些接口的具體實(shí)現(xiàn)由開(kāi)發(fā)者提供。
URI介紹
Data的提供方和使用方都通過(guò)URI(Uniform Resource Identifier)來(lái)標(biāo)識(shí)一個(gè)具體的數(shù)據(jù),例如數(shù)據(jù)庫(kù)中的某個(gè)表或磁盤上的某個(gè)文件。HarmonyOS的URI仍基于URI通用標(biāo)準(zhǔn),格式如下:
scheme:協(xié)議方案名,固定為“dataability”,代表Data Ability所使用的協(xié)議類型 authority:設(shè)備ID。如果為跨設(shè)備場(chǎng)景,則為目標(biāo)設(shè)備的ID;如果為本地設(shè)備場(chǎng)景,則不需要填寫。 path:資源的路徑信息,代表特定資源的位置信息。 query:查詢參數(shù)。f ragment:可以用于指示要訪問(wèn)的子資源。
URI示例:
跨設(shè)備場(chǎng)景:dataability://device_id/com.domainname.dataability.persondata/person/10 本地設(shè)備:dataability:///com.domainname.dataability.persondata/person/10
2
創(chuàng)建Data
使用Data模板的Ability形式仍然是Ability,因此,開(kāi)發(fā)者需要為應(yīng)用添加一個(gè)或多個(gè)Ability的子類,來(lái)提供程序與其他應(yīng)用之間的接口。Data為結(jié)構(gòu)化數(shù)據(jù)和文件提供了不同API接口供用戶使用,因此,開(kāi)發(fā)者需要首先確定好使用何種類型的數(shù)據(jù)。
本章節(jié)主要講述了創(chuàng)建Data的基本步驟和需要使用的接口。Data提供方可以自定義數(shù)據(jù)的增、刪、改、查,以及文件打開(kāi)等功能,并對(duì)外提供這些接口。
確定數(shù)據(jù)存儲(chǔ)方式
確定數(shù)據(jù)的存儲(chǔ)方式,Data支持以下兩種數(shù)據(jù)形式:
文件數(shù)據(jù):如文本、圖片、音樂(lè)等。 結(jié)構(gòu)化數(shù)據(jù):如數(shù)據(jù)庫(kù)等。
實(shí)現(xiàn)UserDataAbility
UserDataAbility用于接收其他應(yīng)用發(fā)送的請(qǐng)求,提供外部程序訪問(wèn)的入口,從而實(shí)現(xiàn)應(yīng)用間的數(shù)據(jù)訪問(wèn)。
實(shí)現(xiàn)UserDataAbility,需要在“Project”窗口當(dāng)前工程的主目錄(“entry > src > main > java > com.xxx.xxx”)選擇“File > New > Ability > Empty Data Ability”,設(shè)置“Data Name”后完成UserDataAbility的創(chuàng)建。
Data提供了文件存儲(chǔ)和數(shù)據(jù)庫(kù)存儲(chǔ)兩組接口供用戶使用。
文件存儲(chǔ)
開(kāi)發(fā)者需要在Data中重寫FileDescriptor openFile(Uri uri, String mode)方法來(lái)操作文件:uri為客戶端傳入的請(qǐng)求目標(biāo)路徑;mode為開(kāi)發(fā)者對(duì)文件的操作選項(xiàng),可選方式包含“r”(讀), “w”(寫), “rw”(讀寫)等。
ohos.rpc.MessageParcel類提供了一個(gè)靜態(tài)方法,用于獲取MessageParcel實(shí)例。開(kāi)發(fā)者可通過(guò)獲取到的MessageParcel實(shí)例,使用dupFileDescriptor()函數(shù)復(fù)制待操作文件流的文件描述符,并將其返回,供遠(yuǎn)端應(yīng)用訪問(wèn)文件。
示例:根據(jù)傳入的uri打開(kāi)對(duì)應(yīng)的文件。
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");
@Override
public FileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// 創(chuàng)建messageParcel
MessageParcel messageParcel = MessageParcel.obtain();
File file = new File(uri.getDecodedPathList().get(0)); //get(0)是獲取URI完整字段中查詢參數(shù)字段。
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);
}
數(shù)據(jù)庫(kù)存儲(chǔ)
1、初始化數(shù)據(jù)庫(kù)連接。系統(tǒng)會(huì)在應(yīng)用啟動(dòng)時(shí)調(diào)用onStart()方法創(chuàng)建Data實(shí)例。在此方法中,開(kāi)發(fā)者應(yīng)該創(chuàng)建數(shù)據(jù)庫(kù)連接,并獲取連接對(duì)象,以便后續(xù)和數(shù)據(jù)庫(kù)進(jìn)行操作。為了避免影響應(yīng)用啟動(dòng)速度,開(kāi)發(fā)者應(yīng)當(dāng)盡可能將非必要的耗時(shí)任務(wù)推遲到使用時(shí)執(zhí)行,而不是在此方法中執(zhí)行所有初始化。
示例:初始化的時(shí)候連接數(shù)據(jù)庫(kù)。
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;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
DatabaseHelper manager = new DatabaseHelper(this);
ormContext = manager.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);
}
2、編寫數(shù)據(jù)庫(kù)操作方法。Ability定義了6個(gè)方法供用戶處理對(duì)數(shù)據(jù)庫(kù)表數(shù)據(jù)的增刪改查。這6個(gè)方法在Ability中已默認(rèn)實(shí)現(xiàn),開(kāi)發(fā)者可按需重寫。
| 方法 | 描述 |
|---|---|
| ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) | 查詢數(shù)據(jù)庫(kù) |
| int insert(Uri uri, ValuesBucket value) | 向數(shù)據(jù)庫(kù)中插入單條數(shù)據(jù) |
| int batchInsert(Uri uri, ValuesBucket[] values) | 向數(shù)據(jù)庫(kù)中插入多條數(shù)據(jù) |
| int delete(Uri uri, DataAbilityPredicates predicates) | 刪除一條或多條數(shù)據(jù) |
| int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) | 更新數(shù)據(jù)庫(kù) |
| DataAbilityResult[] executeBatch(ArrayList | 批量操作數(shù)據(jù)庫(kù) |
3、批量操作數(shù)據(jù)庫(kù) 這些方法的使用說(shuō)明如下:
query() 該方法接收三個(gè)參數(shù),分別是查詢的目標(biāo)路徑,查詢的列名,以及查詢條件,查詢條件由類DataAbilityPredicates構(gòu)建。根據(jù)傳入的列名和查詢條件查詢用戶表的代碼示例如下:
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to query, ormContext is null");
return null;
}
// 查詢數(shù)據(jù)庫(kù)
OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
ResultSet resultSet = ormContext.query(ormPredicates, columns);
if (resultSet == null) {
HiLog.info(LABEL_LOG, "resultSet is null");
}
// 返回結(jié)果
return resultSet;
}
insert() 該方法接收兩個(gè)參數(shù),分別是插入的目標(biāo)路徑和插入的數(shù)據(jù)值。其中,插入的數(shù)據(jù)由ValuesBucket封裝,服務(wù)端可以從該參數(shù)中解析出對(duì)應(yīng)的屬性,然后插入到數(shù)據(jù)庫(kù)中。此方法返回一個(gè)int類型的值用于標(biāo)識(shí)結(jié)果。接收到傳過(guò)來(lái)的用戶信息并把它保存到數(shù)據(jù)庫(kù)中的代碼示例如下:
public int insert(Uri uri, ValuesBucket value) {
// 參數(shù)校驗(yàn)
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to insert, ormContext is null");
return -1;
}
// 構(gòu)造插入數(shù)據(jù)
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"));
// 插入數(shù)據(jù)庫(kù)
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() 該方法為批量插入方法,接收一個(gè)ValuesBucket數(shù)組用于單次插入一組對(duì)象。它的作用是提高插入多條重復(fù)數(shù)據(jù)的效率。該方法系統(tǒng)已實(shí)現(xiàn),開(kāi)發(fā)者可以直接調(diào)用。 delete() 該方法用來(lái)執(zhí)行刪除操作。刪除條件由類DataAbilityPredicates構(gòu)建,服務(wù)端在接收到該參數(shù)之后可以從中解析出要?jiǎng)h除的數(shù)據(jù),然后到數(shù)據(jù)庫(kù)中執(zhí)行。根據(jù)傳入的條件刪除用戶表數(shù)據(jù)的代碼示例如下:
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() 此方法用來(lái)執(zhí)行更新操作。用戶可以在ValuesBucket參數(shù)中指定要更新的數(shù)據(jù),在DataAbilityPredicates中構(gòu)建更新的條件等。更新用戶表的數(shù)據(jù)的代碼示例如下:
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() 此方法用來(lái)批量執(zhí)行操作。DataAbilityOperation中提供了設(shè)置操作類型、數(shù)據(jù)和操作條件的方法,用戶可自行設(shè)置自己要執(zhí)行的數(shù)據(jù)庫(kù)操作。該方法系統(tǒng)已實(shí)現(xiàn),開(kāi)發(fā)者可以直接調(diào)用。
說(shuō)明 上述代碼示例中,初始化了數(shù)據(jù)庫(kù)類BookStore.class,并通過(guò)實(shí)體類User.class對(duì)該數(shù)據(jù)庫(kù)的表User進(jìn)行增刪改查操作。關(guān)于對(duì)象關(guān)系映射數(shù)據(jù)庫(kù)的具體邏輯,以及示例中BookStore.class與User.class的邏輯關(guān)系,可參考“對(duì)象關(guān)系映射數(shù)據(jù)庫(kù)開(kāi)發(fā)指導(dǎo)”。
3
注冊(cè)UserDataAbility
和Service類似,開(kāi)發(fā)者必須在配置文件中注冊(cè)Data。配置文件中該字段在創(chuàng)建Data Ability時(shí)會(huì)自動(dòng)創(chuàng)建,name與創(chuàng)建的Data Ability一致。需要關(guān)注以下屬性:
type: 類型設(shè)置為data uri: 對(duì)外提供的訪問(wèn)路徑,全局唯一 permissions: 訪問(wèn)該data ability時(shí)需要申請(qǐng)的訪問(wèn)權(quán)限 說(shuō)明 如果權(quán)限非系統(tǒng)權(quán)限,需要在配置文件中進(jìn)行自定義。請(qǐng)參考權(quán)限開(kāi)發(fā)指導(dǎo)中關(guān)于“自定義權(quán)限”的相關(guān)說(shuō)明。
{
"name": ".UserDataAbility",
"type": "data",
"visible": true,
"uri": "dataability://com.example.myapplication5.DataAbilityTest",
"permissions": [
"com.example.myapplication5.DataAbility.DATA"
]
}
4
訪問(wèn)Data
開(kāi)發(fā)者可以通過(guò)DataAbilityHelper類來(lái)訪問(wèn)當(dāng)前應(yīng)用或其他應(yīng)用提供的共享數(shù)據(jù)。DataAbilityHelper作為客戶端,與提供方的Data進(jìn)行通信。Data接收到請(qǐng)求后,執(zhí)行相應(yīng)的處理,并返回結(jié)果。DataAbilityHelper提供了一系列與Data Ability對(duì)應(yīng)的方法。
下面介紹DataAbilityHelper具體的使用步驟。
聲明使用權(quán)限
如果待訪問(wèn)的Data聲明了訪問(wèn)需要權(quán)限,則訪問(wèn)此Data需要在配置文件中聲明需要此權(quán)限。聲明請(qǐng)參考權(quán)限申請(qǐng)字段說(shuō)明。
"reqPermissions": [
{
"name": "com.example.myapplication5.DataAbility.DATA"
},
// 訪問(wèn)文件還需要添加訪問(wèn)存儲(chǔ)讀寫權(quán)限
{
"name": "ohos.permission.READ_USER_STORAGE"
},
{
"name": "ohos.permission.WRITE_USER_STORAGE"
}
]
創(chuàng)建DataAbilityHelper
DataAbilityHelper為開(kāi)發(fā)者提供了creator()方法來(lái)創(chuàng)建DataAbilityHelper實(shí)例。該方法為靜態(tài)方法,有多個(gè)重載。最常見(jiàn)的方法是通過(guò)傳入一個(gè)context對(duì)象來(lái)創(chuàng)建DataAbilityHelper對(duì)象。
獲取helper對(duì)象示例:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
訪問(wèn)Data Ability
DataAbilityHelper為開(kāi)發(fā)者提供了一系列的接口來(lái)訪問(wèn)不同類型的數(shù)據(jù)(文件、數(shù)據(jù)庫(kù)等)。
訪問(wèn)文件 DataAbilityHelper為開(kāi)發(fā)者提供了FileDescriptor openFile(Uri uri, String mode)方法來(lái)操作文件。此方法需要傳入兩個(gè)參數(shù),其中uri用來(lái)確定目標(biāo)資源路徑,mode用來(lái)指定打開(kāi)文件的方式,可選方式包含“r”(讀), “w”(寫), “rw”(讀寫),“wt”(覆蓋寫),“wa”(追加寫),“rwt”(覆蓋寫且可讀)。該方法返回一個(gè)目標(biāo)文件的FD(文件描述符),把文件描述符封裝成流,開(kāi)發(fā)者就可以對(duì)文件流進(jìn)行自定義處理。訪問(wèn)文件示例:
// 讀取文件描述符
FileDescriptor fd = helper.openFile(uri, "r");
FileInputStream fis = new FileInputStream(fd);
// 使用文件描述符封裝成的文件流,進(jìn)行文件操作
訪問(wèn)數(shù)據(jù)庫(kù) DataAbilityHelper為開(kāi)發(fā)者提供了增、刪、改、查以及批量處理等方法來(lái)操作數(shù)據(jù)庫(kù)。說(shuō)明 對(duì)數(shù)據(jù)庫(kù)的操作方法,詳見(jiàn)數(shù)據(jù)管理中各數(shù)據(jù)庫(kù)類型的開(kāi)發(fā)指南。
| 方法 | 描述 |
|---|---|
| ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) | 查詢數(shù)據(jù)庫(kù) |
| int insert(Uri uri, ValuesBucket value) | 向數(shù)據(jù)庫(kù)中插入單條數(shù)據(jù) |
| int batchInsert(Uri uri, ValuesBucket[] values) | 向數(shù)據(jù)庫(kù)中插入多條數(shù)據(jù) |
| int delete(Uri uri, DataAbilityPredicates predicates) | 刪除一條或多條數(shù)據(jù) |
| int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) | 更新數(shù)據(jù)庫(kù) |
| DataAbilityResult[] executeBatch(ArrayList | 批量操作數(shù)據(jù)庫(kù) |
這些方法的使用說(shuō)明如下:
query() 查詢方法,其中uri為目標(biāo)資源路徑,columns為想要查詢的字段。開(kāi)發(fā)者的查詢條件可以通過(guò)DataAbilityPredicates來(lái)構(gòu)建。查詢用戶表中id在101-103之間的用戶,并把結(jié)果打印出來(lái),代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 構(gòu)造查詢條件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
// 進(jìn)行查詢
ResultSet resultSet = helper.query(uri, columns, predicates);
// 處理結(jié)果
resultSet.goToFirstRow();
do {
// 在此處理ResultSet中的記錄;
} while(resultSet.goToNextRow());
insert() 新增方法,其中uri為目標(biāo)資源路徑,ValuesBucket為要新增的對(duì)象。插入一條用戶信息的代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 構(gòu)造插入數(shù)據(jù)
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.insert(uri, valuesBucket);
batchInsert() 批量插入方法,和insert()類似。批量插入用戶信息的代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 構(gòu)造插入數(shù)據(jù)
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() 刪除方法,其中刪除條件可以通過(guò)DataAbilityPredicates來(lái)構(gòu)建。刪除用戶表中id在101-103之間的用戶,代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 構(gòu)造刪除條件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
helper.delete(uri, predicates);
update() 更新方法,更新數(shù)據(jù)由ValuesBucket傳入,更新條件由DataAbilityPredicates來(lái)構(gòu)建。更新id為102的用戶,代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 構(gòu)造更新條件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo("userId", 102);
// 構(gòu)造更新數(shù)據(jù)
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.update(uri, valuesBucket, predicates);
executeBatch() 此方法用來(lái)執(zhí)行批量操作。DataAbilityOperation中提供了設(shè)置操作類型、數(shù)據(jù)和操作條件的方法,開(kāi)發(fā)者可自行設(shè)置自己要執(zhí)行的數(shù)據(jù)庫(kù)操作。插入多條數(shù)據(jù)的代碼示例如下:
DataAbilityHelper helper = DataAbilityHelper.creator(abilityObj, insertUri);
// 構(gòu)造批量操作
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);
5
相關(guān)實(shí)例
針對(duì)Data Ability開(kāi)發(fā),有以下示例工程可供參考:
DataAbility 本示例演示了如何使用Data Ability對(duì)數(shù)據(jù)庫(kù)進(jìn)行增、刪、改、查,以及讀取文本文件。針對(duì)Data Ability開(kāi)發(fā),有以下Codelabs可供參考:
關(guān)系型數(shù)據(jù)庫(kù) 基于Data Ability的關(guān)系型數(shù)據(jù)庫(kù)和數(shù)據(jù)管理能力,實(shí)現(xiàn)數(shù)據(jù)庫(kù)相關(guān)應(yīng)用服務(wù)的快速開(kāi)發(fā)。
往期推薦

