本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
本手册长期维护,持续更新中...
一. 数据持久化
1.1 用户首选项Preferences (@ohos.data.preferences)
Preferences 提供 Key-Value 键值型的数据处理能力,实例代码如下:
ts
let options: dataPreferences.Options = { name: 'myStore' };
preferences = dataPreferences.getPreferencesSync(context, options);
preferences.putSync('startup', 'auto');//写数据
preferences.getSync('startup', 'default');//读数据
preferences.deleteSync('startup');//删除数据
//将当前Preferences实例的数据异步存储到用户首选项持久化文件中。
preferences.flush((error: BusinessError) => {
if (error) {
console.log(`持久化失败 error = ${JSON.stringify(error)}`);
return;
}
console.log("持久化成功");
});
注:putSync 之后一定要调用 flush 来实现持久化,否者只是修改了内存中的 preferences 实例
1.2 键值型数据库(@ohos.data.distributedKVStore)
第一步:获取 KVManager 实例
ts
// 导入模块
import distributedKVStore from '@ohos.data.distributedKVStore';
let context = this.context;
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
};
try {
// 创建KVManager实例
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// 继续创建获取数据库
} catch (e) {
let error = e as BusinessError;
console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);
}
第二步:创建并获取键值数据库
ts
let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
try {
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false,
// kvStoreType不填时,默认创建多设备协同数据库
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
// 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {
if (err) {
console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
// 请确保获取到键值数据库实例后,再进行相关数据操作
});
} catch (e) {
let error = e as BusinessError;
console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
}
if (kvStore !== undefined) {
kvStore = kvStore as distributedKVStore.SingleKVStore;
//进行后续操作
//...
}
第三步:操作数据库
ts
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
//新增
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {});
//删除
kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {});
//查找
kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {});
1.3 关系型数据库(@ohos.data.relationalStore)
基于SQLite组件,适用于存储包含复杂关系数据的场景
第一步:获取RdbStore
ts
import relationalStore from '@ohos.data.relationalStore'; // 导入模块
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
const STORE_CONFIG :relationalStore.StoreConfig= {
name: 'RdbTest.db', // 数据库文件名
securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
dataGroupId: 'dataGroupID', // 可选参数表示为应用组ID,需向应用市场获取。指定在此Id对应的沙箱路径下创建实例,不填时,默认在本应用沙箱目录下创建。
customDir: 'customDir/subCustomDir' // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
};
// 判断数据库版本,如果不匹配则需进行升降级操作
// 假设当前数据库版本为3,表结构:EMPLOYEE (NAME, AGE, SALARY, CODES)
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // 建表Sql语句
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in getting RdbStore.');
// 当数据库创建时,数据库默认版本为0
if (store.version === 0) {
store.executeSql(SQL_CREATE_TABLE); // 创建数据表
// 设置数据库的版本,入参为大于0的整数
store.version = 3;
}
// 如果数据库版本不为0且和当前数据库版本不匹配,需要进行升降级操作
// 当数据库存在并假定版本为1时,例应用从某一版本升级到当前版本,数据库需要从1版本升级到2版本
if (store.version === 1) {
// version = 1:表结构:EMPLOYEE (NAME, SALARY, CODES, ADDRESS) => version = 2:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS)
if (store !== undefined) {
(store as relationalStore.RdbStore).executeSql('ALTER TABLE EMPLOYEE ADD COLUMN AGE INTEGER');
store.version = 2;
}
}
// 当数据库存在并假定版本为2时,例应用从某一版本升级到当前版本,数据库需要从2版本升级到3版本
if (store.version === 2) {
// version = 2:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS) => version = 3:表结构:EMPLOYEE (NAME, AGE, SALARY, CODES)
if (store !== undefined) {
(store as relationalStore.RdbStore).executeSql('ALTER TABLE EMPLOYEE DROP COLUMN ADDRESS TEXT');
store.version = 3;
}
}
});
// 请确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
}
}
第二步:操作数据库
ts
import { ValuesBucket } from '@ohos.data.ValuesBucket';
let store: relationalStore.RdbStore | undefined = undefined;
let value1 = 'Lisa';
let value2 = 18;
let value3 = 100.5;
let value4 = new Uint8Array([1, 2, 3, 4, 5]);
const valueBucket: ValuesBucket = {
'NAME': value1,
'AGE': value2,
'SALARY': value3,
'CODES': value4,
};
if (store !== undefined && store as relationalStore.RdbStore) {
// 调用insert()新增数据
store.insert('EMPLOYEE', valueBucket, (err: BusinessError, rowId: number) => {})
// 调用update()修改数据
let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建表'EMPLOYEE'的predicates
predicates.equalTo('NAME', 'Lisa'); // 匹配表'EMPLOYEE'中'NAME'为'Lisa'的字段
store.update(valueBucket, predicates, (err: BusinessError, rows: number) => {})
// 调用delete()删除数据
predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Lisa');
store.delete(predicates, (err: BusinessError, rows: number) => {})
// 调用query()查询数据
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Rose');
store.query(predicates, ['ID', 'NAME', 'AGE', 'SALARY'], (err: BusinessError, resultSet) => {
if (err) {
console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
return;
}
// resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
while (resultSet.goToNextRow()) {
const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
const name = resultSet.getString(resultSet.getColumnIndex('NAME'));
const age = resultSet.getLong(resultSet.getColumnIndex('AGE'));
const salary = resultSet.getDouble(resultSet.getColumnIndex('SALARY'));
console.info(`id=${id}, name=${name}, age=${age}, salary=${salary}`);
}
// 释放数据集的内存
resultSet.close();
})
}
1.4 文件(@ohos.file.fs)
获取文件路径
ts
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;
创建文件
ts
function createFile(): void {
// 新建并打开文件
let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 写入一段内容至文件
let writeLen = fs.writeSync(file.fd, "Try to write str.");
console.info("The length of str is: " + writeLen);
// 从文件读取一段内容
let arrayBuffer = new ArrayBuffer(1024);
let readOptions: ReadOptions = {
offset: 0,
length: arrayBuffer.byteLength
};
let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
let buf = buffer.from(arrayBuffer, 0, readLen);
console.info("the content of file: " + buf.toString());
// 关闭文件
fs.closeSync(file);
}
读取文件内容并写入到另一个文件
ts
function readWriteFile(): void {
// 打开文件
let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
let destFile = fs.openSync(filesDir + '/destFile.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 读取源文件内容并写入至目的文件
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
let readLen = fs.readSync(srcFile.fd, buf, readOptions);
while (readLen > 0) {
readSize += readLen;
let writeOptions = {
length: readLen
};
fs.writeSync(destFile.fd, buf, writeOptions);
readOptions.offset = readSize;
readLen = fs.readSync(srcFile.fd, buf, readOptions);
}
// 关闭文件
fs.closeSync(srcFile);
fs.closeSync(destFile);
}
以流的形式读取文件
ts
async function readWriteFileWithStream(): Promise<void> {
// 打开文件流
let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+');
let outputStream = fs.createStreamSync(filesDir + '/destFile.txt', "w+");
// 以流的形式读取源文件内容并写入目的文件
let bufSize = 4096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
let readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
while (readLen > 0) {
await outputStream.write(buf);
readOptions.offset = readSize;
readLen = await inputStream.read(buf, readOptions);
readSize += readLen;
}
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
查看文件列表
ts
// 查看文件列表
function getListFile(): void {
let listFileOption: ListFileOptions = {
recursion: false,
listNum: 0,
filter: {
suffix: [".png", ".jpg", ".txt"],
displayName: ["test*"],
fileSizeOver: 0,
lastModifiedAfter: new Date(0).getTime()
}
};
let files = fs.listFileSync(filesDir, listFileOption);
for (let i = 0; i < files.length; i++) {
console.info(`The name of file: ${files[i]}`);
}
}
二. 网络
2.1 Http请求(@ohos.net.http):
- 调用createHttp()方法,创建一个HttpRequest对象。
- 调用该对象的on()方法,订阅http响应头事件,此接口会比request请求先返回。可以根据业务需要订阅此消息。
- 调用该对象的request()方法,传入http请求的url地址和可选参数,发起网络请求。
- 按照实际业务需要,解析返回结果。
- 调用该对象的off()方法,取消订阅http响应头事件。
- 当该请求使用完毕时,调用destroy()方法主动销毁。
ts
let httpRequest = http.createHttp();
// 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息
httpRequest.on('headersReceive', (header) => {
console.info('header: ' + JSON.stringify(header));
});
httpRequest.request(
"EXAMPLE_URL",
{
method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET
header: {
'Content-Type': 'application/json'
},
// 当使用POST请求时此字段用于传递请求体内容,具体格式与服务端协商确定
extraData: "data to send",
expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
usingCache: true, // 可选,默认为true
priority: 1, // 可选,默认为1
connectTimeout: 60000, // 可选,默认为60000ms
readTimeout: 60000, // 可选,默认为60000ms
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
usingProxy: false, // 可选,默认不使用网络代理,自API 10开始支持该属性
caPath:'/path/to/cacert.pem', // 可选,默认使用系统预制证书,自API 10开始支持该属性
clientCert: { // 可选,默认不使用客户端证书,自API 11开始支持该属性
certPath: '/path/to/client.pem', // 默认不使用客户端证书,自API 11开始支持该属性
keyPath: '/path/to/client.key', // 若证书包含Key信息,传入空字符串,自API 11开始支持该属性
certType: http.CertType.PEM, // 可选,默认使用PEM,自API 11开始支持该属性
keyPassword: "passwordToKey" // 可选,输入key文件的密码,自API 11开始支持该属性
},
multiFormDataList: [ // 可选,仅当Header中,'content-Type'为'multipart/form-data'时生效,自API 11开始支持该属性
{
name: "Part1", // 数据名,自API 11开始支持该属性
contentType: 'text/plain', // 数据类型,自API 11开始支持该属性
data: 'Example data', // 可选,数据内容,自API 11开始支持该属性
remoteFileName: 'example.txt' // 可选,自API 11开始支持该属性
}, {
name: "Part2", // 数据名,自API 11开始支持该属性
contentType: 'text/plain', // 数据类型,自API 11开始支持该属性
// data/app/el2/100/base/com.example.myapplication/haps/entry/files/fileName.txt
filePath: `${getContext(this).filesDir}/fileName.txt`, // 可选,传入文件路径,自API 11开始支持该属性
remoteFileName: 'fileName.txt' // 可选,自API 11开始支持该属性
}
]
}, (err: BusinessError, data: http.HttpResponse) => {
if (!err) {
// data.result为HTTP响应内容,可根据业务需要进行解析
console.info('Result:' + JSON.stringify(data.result));
console.info('code:' + JSON.stringify(data.responseCode));
// data.header为HTTP响应头,可根据业务需要进行解析
console.info('header:' + JSON.stringify(data.header));
console.info('cookies:' + JSON.stringify(data.cookies)); // 8+
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
} else {
console.error('error:' + JSON.stringify(err));
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
}
}
);
2.2 WebView(@ohos.web.webview)
ts
import web_webview from '@ohos.web.webview'
controller: web_webview.WebviewController = new web_webview.WebviewController()
//加载在线网页
Web({ src: 'www.example.com', controller: this.controller, renderMode: RenderMode.SYNC_RENDER })
// 通过$rawfile加载本地资源文件。
Web({ src: $rawfile("index.html"), controller: this.controller })
// 通过resource协议加载本地资源文件。
Web({ src: "resource://rawfile/index.html", controller: this.controller })
三. 设备唯一标识符
可以使用ADs kit中的广告ID(OAID)来作为设备唯一标识符
ts
import identifier from '@ohos.identifier.oaid';
try {
identifier.getOAID().then((data) => {
const oaid: string = data;
}).catch((err: BusinessError) => {
...
})
} catch (err) {
...
}
try {
identifier.getOAID((err: BusinessError, data: string) => {
if (err.code) {
...
} else {
const oaid: string = data;
}
});
} catch (err) {
...
}
四. 日志打印
4.1 Hilog日志
HiLog中定义了DEBUG、INFO、WARN、ERROR、FATAL五种日志级别,并提供了对应的方法输出不同级别的日志
接口名 | 功能描述 |
---|---|
isLoggable(domain: number, tag: string, level: LogLevel) | 在打印日志前调用该接口,检查指定领域标识、日志标识和级别的日志是否可以打印。 |
debug(domain: number, tag: string, format: string, ...args: any[]) | 输出DEBUG级别日志。仅用于应用/服务调试。在DevEco Studio的terminal窗口或cmd里,通过命令"hdc shell hilogcat"设置可打印日志的等级为DEBUG。 |
info(domain: number, tag: string, format: string, ...args: any[]) | 输出INFO级别日志。表示普通的信息。 |
warn(domain: number, tag: string, format: string, ...args: any[]) | 输出WARN级别日志。表示存在警告。 |
error(domain: number, tag: string, format: string, ...args: any[]) | 输出ERROR级别日志。表示存在错误。 |
fatal(domain: number, tag: string, format: string, ...args: any[]) | 输出FATAL级别日志。表示出现致命错误、不可恢复错误。 |
4.2 Console日志
ts
const number = 5;
console.debug('count: %d', number); // 格式化输出替换message中的文本。
// count: 5
console.debug('count:', number); // 打印message以及其余信息
// count: 5
console.debug('count:'); // 仅打印message
// count:
console.log('count: %d', number);
console.info('count: %d', number);
console.warn('warn: %d', str);
console.error('error: %d', str);
五. 命令行工具
HDC常用命令及和Android ADB命令的对比(以下表格来源于网络)
HDC | ADB | 说明 |
---|---|---|
hdc -h | adb --help | 查看帮助 |
hdc -v | adb --version | 查看版本 |
hdc list targets | adb devices | 查看连接设备 |
hdc kill | adb kill-server | 结束服务 |
hdc kill -r | adb start-server | 启动服务 |
hdc app install 安装包路径 | adb install 安装包路径 | 安装应用 |
hdc app uninstall package | adb uninstall package | 卸载应用 |
hdc hilog | adb logcat | 抓取log |
hdc shell hilogcat >log.log | adb shell logcat >log.log | 抓取log并保存 |
hdc shell reboot | adb reboot | 重启设备 |
hdc shell bm get -u | adb shell bm get -u | 获取UUID |
hdc file recv REMOTE... LOCAL | adb pull REMOTE... LOCAL | 接收文件 REMOTE:手机 LOCAL:PC |
hdc file send LOCAL... REMOTE | adb push LOCAL... REMOTE | 发送文件 |
hdc shell screencap filename | adb shell screencap filename | 截屏 |
hdc shell screenrecord filename | adb shell screenrecord filename | 录屏 |