鸿蒙开发效率手册

本文为稀土掘金技术社区首发签约文章,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 录屏
相关推荐
HerayChen31 分钟前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野32 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing112334 分钟前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
yilylong34 分钟前
鸿蒙(Harmony)实现滑块验证码
华为·harmonyos·鸿蒙
baby_hua34 分钟前
HarmonyOS第一课——DevEco Studio的使用
华为·harmonyos
GIS程序媛—椰子1 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0011 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试
小黄人软件1 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio
木舟10091 小时前
ffmpeg重复回听音频流,时长叠加问题
前端