鸿蒙开发效率手册

本文为稀土掘金技术社区首发签约文章,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):

  1. 调用createHttp()方法,创建一个HttpRequest对象。
  2. 调用该对象的on()方法,订阅http响应头事件,此接口会比request请求先返回。可以根据业务需要订阅此消息。
  3. 调用该对象的request()方法,传入http请求的url地址和可选参数,发起网络请求。
  4. 按照实际业务需要,解析返回结果。
  5. 调用该对象的off()方法,取消订阅http响应头事件。
  6. 当该请求使用完毕时,调用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 录屏
相关推荐
__BMGT()几秒前
C++ QT图片查看器
前端·c++·qt
zkmall4 分钟前
Java + 鸿蒙双引擎:ZKmall开源商城如何定义下一代B2C商城技术标准?
java·开源·harmonyos
橙子1991101617 分钟前
在 Kotlin 中,什么是解构,如何使用?
android·开发语言·kotlin
未来之窗软件服务32 分钟前
solidwors插件 开发————仙盟创梦IDE
前端·javascript·数据库·ide·仙盟创梦ide
lqj_本人36 分钟前
鸿蒙OS&UniApp实现视频播放与流畅加载:打造完美的移动端视频体验#三方框架 #Uniapp
uni-app·音视频·harmonyos
Varpb1 小时前
【vue】【环境配置】项目无法npm run serve,显示node版本过低
前端·vue.js·npm
读心悦1 小时前
CSS 溢出内容处理、可见性控制与盒类型设置深度解析
前端·css
Minyy111 小时前
Vue3指令(二)--v-text、v-html数据渲染,计算属性
前端·javascript·vue.js·前端框架·vue·html
交叉编译之王 hahaha1 小时前
RK3568平台OpenHarmony系统移植可行性评估
华为·harmonyos
个人开发-胡涂涂1 小时前
ECMAScript标准:JavaScript的核心
前端·javascript·ecmascript