Harmoney北向开发入门篇(四)

1,823 阅读25分钟

网络请求

在ArkTS中,网络管理模块主要提供以下功能:

在使用网络管理模块的相关功能时,需要请求相应的权限。

权限名说明
ohos.permission.GET_NETWORK_INFO获取网络连接信息。
ohos.permission.SET_NETWORK_INFO修改网络连接状态。
ohos.permission.INTERNET允许程序打开网络套接字,进行网络连接。

HTTP数据请求

场景介绍

应用通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。

接口说明

HTTP数据请求功能主要由http模块提供。

使用该功能需要申请ohos.permission.INTERNET权限。

权限申请请参考访问控制(权限)开发指导

涉及的接口如下表,具体的接口说明请参考API文档

接口名功能描述
createHttp()创建一个http请求。
request()根据URL地址,发起HTTP网络请求。
destroy()中断请求任务。
on(type: 'headersReceive')订阅HTTP Response Header 事件。
off(type: 'headersReceive')取消订阅HTTP Response Header 事件。
once('headersReceive')8+订阅HTTP Response Header 事件,但是只触发一次。

用法

  1. 导入http的包,从@ohos.net.http.d.ts中导入http命名空间。
import http from '@ohos.net.http';

  1. 调用createHttp()方法,创建一个HttpRequest对象。
let httpRequest = http.createHttp()
  1. 调用该对象的on()方法,订阅http响应头事件,此接口会比request请求先返回。可以根据业务需要订阅此消息。
// 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息
// 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+
httpRequest.on('headersReceive', (header) => {
  //拿到请求头
  console.info('header: ' + JSON.stringify(header));
});
  1. 调用该对象的request()方法,传入http请求的url地址和可选参数,发起网络请求。
    request函数支持传入三个参数,第一个参数是我们请求的接口域名地址;第二个参数是HttpRequestOptions,我们主要在这个参数对象内定义我们的请求参数;第三个参数是回调函数(异步的),它会在完成请求后返回本次http请求的最终结果,类型为HttpResponse。

    HttpRequestOptions的属性如下:
HttpRequestOptions
名称类型描述
methodRequestMethod请求方式,GET、POST、PUT、DELETE等。
extraDatastringObject请求所携带的参数。
headerObject请求头参数对象。
connectTimeoutnumber连接超时时间,单位毫秒,默认为60_000ms
readTimeoutnumber读取超时时间,同上,默认为60_000ms
expectDataTypehttp.HttpDataType可选,指定返回数据的类型
usingCacheboolean可选,是否使用缓存,默认为true
prioritynumber可选,优先级,默认为1
usingProtocolhttp.HttpProtocol可选,协议类型默认值由系统自动指定

比如,当我们进行get请求时,将method设置为http.RequestMethod.GET,并且在extraData中定义要传递的get参数,即可进行get请求,如:

extraData: {
  "uuid": "c92f1801-594e-4ab5-bfcb-25fb856f01cb",
  "type":h5
}

也可以直接在request的请求地址中,把我们的get参数加上:

httpRequest.request("http://172.16.19.46/server/getpublickey?uuid=c92f1801-594e-4ab5-bfcb-25fb856f01cb&type=h5",...

第三个参数,回调中的HttpResponse参数属性如下:

HttpResponse
名称类型描述
responseCodeResponseCodenumber响应状态码,200为请求成功。
headerObject响应头
cookiesstring响应返回的cookies
resultstringObject响应体,默认是JSON字符串
resultTypeHttpDataType返回值类型
  1. 之后按照实际业务需要,解析返回结果。
    数据的解析,通常我们请求返回的都是JSON字符串,我们通过JSON.parse()函数来转换收到的JSON字符串,将其转换为对象来取数据,比如我们要拿的是json字符串中data字段下的rsa_public_key字段:
// ......
(err, data:http.HttpResponse) => {
  if (!err) {
    //data.result为HTTP响应内容,可根据业务需要进行解析。
    let key:string=JSON.parse(data.result.toString())['data']['rsa_public_key']
  }
  // .....
}
  1. 调用该对象的off()方法,取消订阅http响应头事件。
  2. 当该请求使用完毕时,调用destroy()方法主动销毁。
    官方示例代码是只有在返回请求错误信息后,才会手动执行 off() 和 destroy();可能请求成功后API会自己处理吧?(疑惑)
httpRequest.request(
    // 填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
    "EXAMPLE_URL", 
    {
        method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET
        // 开发者根据自身业务需要添加header字段
        header: {
            'Content-Type': 'application/json'
        },
        // 当使用POST请求时此字段用于传递内容
        extraData: {
            "data": "data to send",
        },
        expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
        usingCache: true, // 可选,默认为true
        priority: 1, // 可选,默认为1
        connectTimeout: 60000, // 可选,默认为60000ms
        readTimeout: 60000, // 可选,默认为60000ms
        usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
    }, (err, data) => {
        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+
        } else {
            console.info('error:' + JSON.stringify(err));
            // 取消订阅HTTP响应头事件
            httpRequest.off('headersReceive');
            // 当该请求使用完毕时,调用destroy方法主动销毁
            httpRequest.destroy();
        }
    }
);

无纸化个人微终端Flutter项目基本信息v1.0

用法二:model层请求 + 函数回调

我们也可以新建一个Model类,将网络请求操作放在Model层执行,Model层请求函数通过回调函数来返回请求结果。

import http from '@ohos.net.http';

class NetWorkDemoModel {
  //发送http的get请求
  sendHttpGetRequest(event: (result: string) => void) {
    // 每一个httpRequest对应一个HTTP请求任务,不可复用
    let httpRequest = http.createHttp()
    // 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息
    // 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+
    httpRequest.on('headersReceive', (header) => {
      //拿到请求头
      console.info('header: ' + JSON.stringify(header));
    });

    httpRequest.request("http://xx.xx.xx.xx/server/getpublickey",
                        {
                          method: http.RequestMethod.GET, // 可选,默认为http.RequestMethod.GET
                          header: { 'Content-Type': 'application/json' },
                          extraData: {
                            'uuid': 'xxxxxxxx-xxxx-xxxxx-xxxxxx',
                            'type': 'xx'
                          },
                          expectDataType: http.HttpDataType.STRING,
                          usingCache: true,
                          priority: 1,
                          connectTimeout: 60000,
                          readTimeout: 60000,
                          usingProtocol: http.HttpProtocol.HTTP1_1,
                        },
                        (err, data: http.HttpResponse) => {
                          if (!err) {
                            //data.result为HTTP响应内容,可根据业务需要进行解析。
                            event(JSON.stringify(data.result))
                            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+
                          } else {
                            console.info("error:" + JSON.stringify(err))
                            httpRequest.off('headersReceive')
                            httpRequest.destroy()
                          }
                        })
  }
}
//创建一个常量
const netWorkDeMode = new NetWorkDemoModel()
//默认导出netWorkDeMode对象
export default netWorkDeMode as NetWorkDemoModel

调用端导入netWorkDeMode对象后,调用sendHttpGetRequest方法,并实现回调函数。

netWorkDemoModel.sendHttpGetRequest((result) => {
                  this.message = result
                  this.httpGetKeyStr = JSON.parse(JSON.parse(result))['data']['rsa_public_key']
                  console.log("httpGetKeyStr:" + this.httpGetKeyStr);
                })

用法三:model层请求 + Promise

model层实现一个请求函数,返回值类型使用Promise<>:Promise用来存放执行异步任务(如网络请求的结果,需要等待的),<>的类型为返回结果的数据类型。

Promise中有两个参数,一个是resole,另一个是reject。resole是请求成功的回调,当我们数据请求成功后,通过resole函数传递数据;如果请求失败,调用reject返回失败信息。

像我们的网络请求,以及数据持久化等异步操作,它们大多数都有对Promise的支持,只是我们的示例中,用的是CallBack回调形式来进行。

sendHttpPostRequest(httpGetKeyStr: string): Promise<http.HttpResponse> {
  return new Promise((resole, reject) => {
    //创建createHttp,并进行网络请求。
    let httpRequest = http.createHttp()
    httpRequest.on('headersReceive', (header) => {
      //拿到请求头
      console.info('header: ' + JSON.stringify(header));
    });
    httpRequest.request("http://xx.xx.xx.xx/server/getpublickey",
      {
        method: http.RequestMethod.POST,
        header: { 'Content-Type': 'application/json' },
        extraData: {
          "account": `${httpGetKeyStr}`,
          "password:": `${httpGetKeyStr}`,
          'uuid': 'c92f1801-594e-4ab5-bfcb-25fb856f01cb',
          'type': 'h5'
        },
        expectDataType: http.HttpDataType.STRING,
        usingCache: true,
        priority: 1,
        connectTimeout: 60_000,
        readTimeout: 60_000,
      }, (err, data) => {
        if (!err) {
          //请求成功:通过resole返回HttpResponse类型数据。
          resole(data)
        } else {
          //请求失败:通过reject返回错误信息
          reject(err)
          httpRequest.off("headersReceive")
          httpRequest.destroy()
        }
      })
  })
}

Promise为我们提供了两个方法,分别是then和catch;then是请求成功回调,catch是请求失败回调。
then回调函数中携带了请求成功后的返回信息;catch中则携带的是请求错误的信息。

netWorkDemoModel.sendHttpPostRequest(this.httpGetKeyStr)
  .then((resp: http.HttpResponse) => {
    //请求成功,解析结果
    this.message = resp.result.toString()
  })
  .catch((err: Error) => {
    //请求失败,处理异常
  })

使用axios三方库进行http网络请求

axios是用来进行http网络请求的三方库,我们接下学习使用axios进行http网络请求。

  1. 首先下载和安装ohpm,这个在安装DevEco Studio时我们就已经安装好了。
    ohpm是鸿蒙的第三方库安装工具;因为axios是第三方库,不是鸿蒙提供的,所以要通过ohpm工具来帮助安装axios三方库。
  2. 找到ohpm目录,进行初始化,执行 init.bat(需要先配置npm环境变量)。
  3. 配置npm和ohpm的环境变量。找到之前下载的nodejs目录配置npm环境变量,和下载的ohpm目录配置ohpm的环境变量。

    检测是否配置成功:
  4. 下载和安装axios:进入项目根目录(也可以在DevEco Studio中的Terminal窗口执行),然后输入 ohpm install @ohos/axios
  5. 开始使用:
    1. 定义接收到的数据Bean类,我们请求得到的数据是:
{
    "result": 200,
    "result_desc": "success",
    "return_message": "操作成功",
    "data": {
        "rsa_public_key": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvbIwyCQhbPMyw1SCHwa0\neEgj4VHiRpoU7VzKgtjF5VDv3FO7+jPHMx05oW6KjEgrioSuBtPoOryGjkgFpUzM\nNaIPH6/hOoKApafyNo7FyZlszQ6fHM5YFY53nDfBoyQznbNjUpmqstGMfNHnq/Pn\nLbo/WgQCgLu6JInQqHZevUFBbibN0MLE9GYHZY1ucp5Tv7yLtPpmxymuLplD4lXA\n56AehL5SA92TPAjMjKxTb++kbPuwvBVaebIAaQSNQGaLhWCaUbmkMqOH/2Ra8rhf\nqqqbH+dffGjs2FjbwVjtvII8ylTrV8e9fCNyIJKT6xthrmbXlNhMiXkMpryQ9H2M\nZwIDAQAB\n-----END PUBLIC KEY-----\n"
    }
}

我们要拿的是rsa_public_key字段,所以定义一个Result接口,其内部有data字段;data字段类型是data,其内部有rsa_public_key。

interface result {
  data:data
}
interface data{
  rsa_public_key: String
}
    1. 发起get请求并处理参数,axios的get函数中,传入了AxiosResponse,reuslt就是我们的数据类型,它会帮我们解析并返回一个数据结果对象,类型为result。
  //发送http的get请求
  sendHttpGetRequest(event: (result: string) => void) {
    // 向给定ID的用户发起请求
    axios.get<result, AxiosResponse<result>, null>('http://172.16.19.46/server/getpublickey', {
      params: {
        'uuid': 'c92f1801-594e-4ab5-bfcb-25fb856f01cb',
        'type': 'h5'
      }
    })
      .then((response: AxiosResponse<result>) => {
        // 处理成功情况
        console.info("id" + response.data.data.rsa_public_key)
        console.info(JSON.stringify(response));
        event(response.data.data.rsa_public_key.toString())
      })
      .catch((error: AxiosError) => {
        // 处理错误情况
        console.info(JSON.stringify(error));
      })
      .then(() => {
        // 总是会执行
      });
  }
鸿蒙三方库平台

我们掌握了axios三方库的导入、下载、使用,那么我们该如何查找其他的三方库呢,鸿蒙给开发者提供了一个三方库中心仓,我们可以在里面查找需要的三方库。

OpenHarmony三方库中心仓目前提供的库还不算很多,期待星河版鸿蒙

WebSocket连接

使用WebSocket建立服务器与客户端的双向连接,需要先通过createWebSocket()方法创建WebSocket对象,然后通过connect()方法连接到服务器。当连接成功后,客户端会收到open事件的回调,之后客户端就可以通过send()方法与服务器进行通信。当服务器发信息给客户端时,客户端会收到message事件的回调。当客户端不要此连接时,可以通过调用close()方法主动断开连接,之后客户端会收到close事件的回调。

若在上述任一过程中发生错误,客户端会收到error事件的回调。

开发步骤

  1. 导入需要的webSocket模块。
import webSocket from '@ohos.net.webSocket';
  1. 创建一个WebSocket连接,返回一个WebSocket对象。
var defaultIpAddress = "ws://";
let ws = webSocket.createWebSocket();
  1. (可选)订阅WebSocket的打开、消息接收、关闭、Error事件。
ws.on('open', (err, value) => {
    console.log("on open, status:" + JSON.stringify(value));
    // 当收到on('open')事件后,可以通过send()方法与服务器进行通信
    ws.send("Hello, server!", (err, value) => {
        if (!err) {
            console.log("Message sent successfully");
        } else {
            console.log("Failed to send the message. Err:" + JSON.stringify(err));
        }
    });
});

ws.on('message', (err, value) => {
    console.log("on message, message:" + value);
    // 当收到服务器的`bye`消息时(此消息字段仅为示意,具体字段需要与服务器协商),主动断开连接
    if (value === 'bye') {
        ws.close((err, value) => {
            if (!err) {
                console.log("Connection closed successfully");
            } else {
                console.log("Failed to close the connection. Err: " + JSON.stringify(err));
            }
        });
    }
});
ws.on('close', (err, value) => {
    console.log("on close, code is " + value.code + ", reason is " + value.reason);
});
ws.on('error', (err) => {
    console.log("on error, error:" + JSON.stringify(err));
});
  1. 根据URL地址,发起WebSocket连接。
ws.connect(defaultIpAddress, (err, value) => {
    if (!err) {
        console.log("Connected successfully");
    } else {
        console.log("Connection failed. Err:" + JSON.stringify(err));
    }
});
  1. 使用完WebSocket连接之后,主动断开连接。
ws.close((err, value) => {
  if (!err) {
    console.log("Connection closed successfully");
  } else {
    console.log("Failed to close the connection. Err: " + JSON.stringify(err));
  }
});

Socket连接

Socket连接主要是通过Socket进行数据传输,支持TCP/UDP/TLS协议。

基本概念

  • Socket:套接字,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
  • TCP:传输控制协议(Transmission Control Protocol)。是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  • UDP:用户数据报协议(User Datagram Protocol)。是一个简单的面向消息的传输层,不需要连接。
  • TLS:安全传输层协议(Transport Layer Security)。用于在两个通信应用程序之间提供保密性和数据完整性。

场景介绍

应用通过Socket进行数据传输,支持TCP/UDP/TLS协议。主要场景有:

  • 应用通过TCP/UDP Socket进行数据传输,我们主要学习了解下这个。
  • 应用通过TLS Socket进行加密数据传输

应用TCP/UDP协议进行通信

UDP与TCP流程大体类似,下面以TCP为例:

  1. import需要的socket模块。
import socket from '@ohos.net.socket';
  1. 创建一个TCPSocket连接,返回一个TCPSocket对象。
// 创建一个TCPSocket连接,返回一个TCPSocket对象。
let tcp = socket.constructTCPSocketInstance();
  1. (可选)订阅TCPSocket相关的订阅事件:消息接收事件、连接状态事件、关闭状态事件。
// 订阅TCPSocket相关的订阅事件
tcp.on('message', value => {
  console.log("on message")
  let buffer = value.message
  let dataView = new DataView(buffer)
  let str = ""
  for (let i = 0; i < dataView.byteLength; ++i) {
    str += String.fromCharCode(dataView.getUint8(i))
  }
  console.log("on connect received:" + str)
});
tcp.on('connect', () => {
  console.log("on connect")
});
tcp.on('close', () => {
  console.log("on close")
});
  1. 绑定IP地址和端口,端口可以指定或由系统随机分配。
  2. 连接到指定的IP地址和端口。
  3. 发送数据。
// 绑定IP地址和端口。
let bindAddress = {
  address: '192.168.xx.xx',
  port: 1234, // 绑定端口,如1234
  family: 1
};
tcp.bind(bindAddress, err => {
  if (err) {
    console.log('bind fail');
    return;
  }
  console.log('bind success');
  // 连接到指定的IP地址和端口。
  let connectAddress = {
    address: '192.168.xx.xx',
    port: 5678, // 连接端口,如5678
    family: 1
  };
  tcp.connect({
    address: connectAddress, timeout: 6000
  }, err => {
    if (err) {
      console.log('connect fail');
      return;
    }
    console.log('connect success');
    // 发送数据
    tcp.send({
      data: 'Hello, server!'
    }, err => {
      if (err) {
        console.log('send fail');
        return;
      }
      console.log('send success');
    })
  });
});
  1. Socket连接使用完毕后,主动关闭。并取消相关事件的订阅
// 连接使用完毕后,主动关闭。取消相关事件的订阅。
// 这里是延迟30秒断开tcp链接。
setTimeout(() => {
  tcp.close((err) => {
    console.log('close socket.')
  });
  tcp.off('message');
  tcp.off('connect');
  tcp.off('close');
}, 30 * 1000);

应用数据持久化

概述

应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。

HarmonyOS标准系统支持典型的存储数据形态,包括用户首选项、键值型数据库、关系型数据库。

  • 用户首选项(Preferences) :通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。
  • 键值型数据库(KV-Store) :一种非关系型数据库,其数据以“键值”对的形式进行组织、索引和存储,其中“键”作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了 解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度 而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。
  • 关系型数据库(RelationalStore) :一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。

用户首选项 (Preferences)

  • 用户首选项(Preferences)为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。
  • Preferences会随着存放的数据量越多而导致应用占用的内存越大,因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(字体大小,是否开启夜间模式)等。
  • 每一个用户首选项实例会对应应用沙箱中的一个持久化文件,我们可以创建很多个用户首选项;当删除实例时,对应的文件也会删除。

约束限制

  • Key键为string类型,要求非空且长度不超过80个字节。
  • 如果Value值为string类型,可以为空,不为空时长度不超过8192个字节。
  • 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。

开发步骤

  1. 导入用户首选项模块。
import dataPreferences from '@ohos.data.preferences';
  1. 要通过用户首选项实现数据持久化,首先要获取Preferences实例。读取指定文件,将数据加载到Preferences实例,用于数据操作。
import UIAbility from '@ohos.app.ability.UIAbility';

class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage) {
    try {
      dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => {
        if (err) {
          console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in getting preferences.');
        // 进行相关数据操作
      })
    } catch (err) {
      console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
    }
  }
}
  1. 写入数据:使用put()方法保存数据到缓存的Preferences实例中。在写入数据后,如有需要,可使用flush()方法将Preferences实例的数据存储到持久化文件。
    当对应的键已经存在时,put()方法会修改其值。
    如果仅需要在键值对不存在时新增键值对,而不修改已有键值对,需使用has()方法检查是否存在对应键值对;如果不关心是否会修改已有键值对,则直接使用put()方法即可。
try {
  preferences.has('startup', function (err, val) {
    if (err) {
      console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    if (val) {
      console.info("The key 'startup' is contained.");
    } else {
      console.info("The key 'startup' does not contain.");
      // 此处以此键值对不存在时写入数据为例
      try {
        preferences.put('startup', 'auto', (err) => {
          if (err) {
            console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);
            return;
          }
          //push成功后可以在此处调用flush()方法写入磁盘实现持久化。
          console.info('Succeeded in putting data.');
        })
      } catch (err) {
        console.error(`Failed to put data. Code: ${err.code},message:${err.message}`);
      }
    }
  })
} catch (err) {
  console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 读取数据。使用get()方法获取数据,即指定键对应的值。如果值为null或者非默认值类型,则返回默认数据。
try {
  preferences.get('startup', 'default', (err, val) => {
    if (err) {
      console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info(`Succeeded in getting value of 'startup'. val: ${val}.`);
  })
} catch (err) {
  console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 删除数据;使用delete()方法删除指定键值对。
try {
  preferences.delete('startup', (err) => {
    if (err) {
      console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info("Succeeded in deleting the key 'startup'.");
  })
} catch (err) {
  console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 数据持久化;应用存入数据到Preferences实例后,可以使用flush()方法实现数据持久化。
try {
  preferences.flush((err) => {
    if (err) {
      console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in flushing.');
  })
} catch (err) {
  console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
}
  1. 订阅数据变更。应用订阅数据变更需要指定observer作为回调方法。订阅的Key值发生变更后,当执行flush()方法时,observer被触发回调。
let observer = function (key) {
  console.info('The key' + key + 'changed.');
}
preferences.on('change', observer);

// 数据产生变更,由'auto'变为'manual'
preferences.put('startup', 'manual', (err) => {
  if (err) {
    console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
    return;
  }
  console.info("Succeeded in putting the value of 'startup'.");
  preferences.flush((err) => {
    if (err) {
      console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in flushing.');
  })
})
  1. 删除指定preferences本地文件。
    使用deletePreferences()方法从内存中移除指定文件对应的Preferences实例,包括内存中的数据。
    若该Preference存在对应的持久化文件,则同时删除该持久化文件,包括指定文件及其备份文件、损坏文件。
  • 调用该接口后,应用不允许再使用该Preferences实例进行数据操作,否则会出现数据一致性问题。
  • 成功删除后,数据及文件将不可恢复。
try {
  dataPreferences.deletePreferences(this.context, 'mystore', (err, val) => {
    if (err) {
      console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in deleting preferences.');
  })
} catch (err) {
  console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
}

封装Preference公共类

在我们使用用户首选项时,通常会创建多个实例,所以我们可以封装一个Preferences工具类,用来管理Preferences实例,以及用户首选项的存储写入操作。

import preferences from '@ohos.data.preferences'

class PreferencesUtil {
  prefMap: Map<string, preferences.Preferences> = new Map()

  loadPreference(context,preferencesName:string){
    //根据preferencesName返回不同的实例。
    preferences.getPreferences(context,preferencesName)
      .then(pref=>{
        this.prefMap[preferencesName]=pref
      })
      .catch(reason=>{
        //异常
      })
  }
  //put...
  //get...
}

const preferencesUtil = new PreferencesUtil()

export default preferencesUtil as PreferencesUtil;

async/await

  • async/await是一种用于处理异步操作的Promise语法糖,使得编写异步代码变得更加简单和易读。
    通过使用async关键字声明一个函数为异步函数,并使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。
  • async函数是一个返回Promise对象的函数,用于表示一个异步操作。
    在async函数内部,可以使用await关键字等待一个Promise对象的解析,并返回其解析值。
    如果一个async函数抛出异常,那么该函数返回的Promise对象将被拒绝,并且异常信息会被传递给Promise对象的onRejected()方法。

我们使用async/await来将上面的 loadPreference 函数代码优化:

  • loadPreference声明为async异步函数,在函数内部通过await来等待Promise对象的解析,并将其解析值存储在pref变量中
  • 直到preferences.getPreferences执行完成后,后面的代码this.prefMap.set(preferencesName,pref)才会继续执行。
import preferences from '@ohos.data.preferences'

class PreferencesUtil {
  prefMap: Map<string, preferences.Preferences> = new Map()

  async loadPreference(context, preferencesName: string) {
    //根据preferencesName返回不同的实例。
    try {
      let pref = await preferences.getPreferences(context, preferencesName)
      this.prefMap.set(preferencesName, pref)
    } catch (err) {
    }
  }

  //存储数据
  async putKeyValue(perfName: string, key: string, value: preferences.ValueType) {
    if (!this.prefMap.has(perfName)) {
      console.log("没有对应的KeyValue数据库实例,请先执行loadPreference");
      return
    }
    try {
      let pref = this.prefMap.get(perfName)
      //写入数据
      await pref.put(key, value)
      //刷入磁盘
      await pref.flush()
      console.log("保存成功");
    } catch (err) {
      console.log("保存失败");
    }
  }
}

const preferencesUtil = new PreferencesUtil()

export default preferencesUtil as PreferencesUtil;

使用:

preferencesUtil.loadPreference((getContext(this) as common.UIAbilityContext),"mystore")
  .then((data)=>{
    //获取成功,开始存数据
    preferencesUtil.putKeyValue("mystore","mode",2)
      .then(data=>{

      })
  })
  .catch(err=>{

  })

键值型数据库(KV-Store)

概述

键值型数据库存储键值对形式的数据,当需要存储的数据没有复杂的关系模型,比如存储商品名称及对应价格、员工工号及今日是否已出勤等,由于数据复杂度低,更容易兼容不同数据库版本和设备类型,因此推荐使用键值型数据库持久化此类数据。

约束限制

  • 设备协同数据库,针对每条记录,Key的长度≤896 Byte,Value的长度<4 MB。
  • 单版本数据库,针对每条记录,Key的长度≤1 KB,Value的长度<4 MB。
  • 每个应用程序最多支持同时打开16个键值型分布式数据库。
  • 键值型数据库事件回调方法中不允许进行阻塞操作,例如修改UI组件。

开发步骤

  1. 若要使用键值型数据库,首先要获取一个KVManager实例,用于管理数据库对象。
// 导入模块
import distributedKVStore from '@ohos.data.distributedKVStore';

// Stage模型
import UIAbility from '@ohos.app.ability.UIAbility';

let kvManager;

export default class EntryAbility extends UIAbility {
  onCreate() {
    let context = this.context;
    const kvManagerConfig = {
      context: context,
      bundleName: 'com.example.datamanagertest'
    };
    try {
      // 创建KVManager实例
      kvManager = distributedKVStore.createKVManager(kvManagerConfig);
      console.info('Succeeded in creating KVManager.');
      // 继续创建获取数据库
    } catch (e) {
      console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
    }
  }
}
  1. 创建并获取键值数据库;配置数据参数,并设置数据库的唯一ID。
try {
  const options = {
    createIfMissing: true, // 当数据库文件不存在时是否创建数据库,默认创建
    encrypt: false, // 设置数据库文件是否加密,默认不加密 
    backup: false, // 设置数据库文件是否备份,默认备份
    kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // 设置要创建的数据库类型,默认为多设备协同数据库
    securityLevel: distributedKVStore.SecurityLevel.S2 // 设置数据库安全级别
  };
  // storeId为数据库唯一标识符
  kvManager.getKVStore('storeId', options, (err, kvStore) => {
    if (err) {
      console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
      return;
    }
    console.info('Succeeded in getting KVStore.');
    // 进行相关数据操作
  });
} catch (e) {
  console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
  1. 调用put()方法向键值数据库中插入数据:
    当Key值存在时,put()方法会修改其值,否则新增一条数据。
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
    if (err !== undefined) {
      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
      return;
    }
    console.info('Succeeded in putting data.');
  });
} catch (e) {
  console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
  1. 调用get()方法获取指定键的值。
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
    if (err !== undefined) {
      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
      return;
    }
    console.info('Succeeded in putting data.');
    kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
      if (err !== undefined) {
        console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
        return;
      }
      console.info(`Succeeded in getting data. data:${data}`);
    });
  });
} catch (e) {
  console.error(`Failed to get data. Code:${e.code},message:${e.message}`);
}
  1. 调用delete()方法删除指定键值的数据。
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
  kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
    if (err !== undefined) {
      console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
      return;
    }
    console.info('Succeeded in putting data.');
    kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
      if (err !== undefined) {
        console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
        return;
      }
      console.info('Succeeded in deleting data.');
    });
  });
} catch (e) {
  console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}

关系型数据库

关系型数据库(RDB)是基于SQLite组件提供的本地数据库,适用于存储包含复杂关系数据的场景,用于管理应用中的结构化数据。

约束限制

  • 系统默认日志方式是WAL(Write Ahead Log)模式,系统默认落盘方式是FULL模式。
  • 数据库中连接池的最大数量是4个,用以管理用户的读操作。
  • 为保证数据的准确性,数据库同一时间只能支持一个写操作。
  • 当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除。

使用方法

  1. 导入关系型数据库模块
import relationalStore from '@ohos.data.relationalStore';
  1. 初始化数据库表,并创建表。
import relationalStore from '@ohos.data.relationalStore';
import common from '@ohos.app.ability.common';


loadSQLite() {
  rdbStore:relationalStore.RdbStore
  
  //rdb关系型数据库配置
  const config = {
    name: "MyApplication.db", //数据库文件名
    securityLevel: relationalStore.SecurityLevel.S1 //数据库安全等级,数值越高,级别越高
  }
  //创建 初始化表操作的SQL语句:创建一个表(如果该表不存在),表的名字为TASK
  const sql = `CREATE TABLE IF NOT EXISTS TASK(
                ID INTEGET PRIMARY KEY,
                NAME TEXT NOT NULL,
                FINISHED bit
                )`
  let context=getContext(this) as common.UIAbilityContext
  //获取rdb实例
  relationalStore.getRdbStore(context,config,(err,rdbStore)=>{
    //得到rdb实例,执行sql语句创建表。
    rdbStore.executeSql(sql)
    //后续的增删改查都是使用rdbStore对象来完成,所以这里先存一下。
    this.rdbStore=rdbStore
  })
}
  1. 新增数据
  insertData(){
    //准备数据
    let task = {id:1,name:'任务1',finished:false}
    //新增数据,将task添加到名称为TASK的表中
    this.rdbStore.insert("TASK",task)
  }
  1. 修改数据
  updateData(id:number){
    //要更新的数据内容
    let task={"finished":true};
    //获取查询条件对象
    let predicates=new relationalStore.RdbPredicates("TASK")
    //进行筛选,匹配ID字段等于参数id的行。
    predicates.equalTo("ID",id)
    //执行更新,修改所匹配行的 finished 字段为true。
    this.rdbStore.update(task,predicates)
  }
  1. 删除数据,删除也是借助predicates来筛选定位,之后调用delete。
  deleteData(id:number){
    //获取查询条件对象
    let predicates=new relationalStore.RdbPredicates("TASK")
    //进行筛选判断,筛选ID字段等于id的行。
    predicates.equalTo("ID",id)
    //执行更新
    this.rdbStore.delete(predicates)
  }
  1. 查询数据,使用query函数:
    //准备数组保存结构
    let tasks:any[]=[]
    //query参数: 第一个参数是匹配的字段,第二个参数是要返回哪些的字段内容。
    this.rdbStore.query(predicates,['ID','NAME','FINISHED'])
      .then(cursor=>{
        //query查询成功后,返回是一个结果集。
        //通过遍历来查询所有结果
        while (!cursor.isAtLastRow){//判断当前指针是否在最后一行
          //cursor默认指向-1行,所以要先跳转下一行。
          cursor.goToNextRow()
          let id=cursor.getLong(cursor.getColumnIndex('ID'))
          let name=cursor.getString(cursor.getColumnIndex('NAME'))
          //boolean类型在数据库中的存储是0和1,所以我们获取boolean类型时,拿取number类型。
          let finished=cursor.getLong(cursor.getColumnIndex('FINISHED'))
          tasks.push({id,name,finished})
        }
      })
      .catch(err=>{})

应用通知

接口说明

不论是何种类型的通知,它们的发布、取消是统一的,都是通过 NotificationManager.publish()cancel()cancelAll()来发布

通知发布接口如下表所示,不同类型的通知由NotificationRequest字段携带不同的信息。

接口名描述
publish(request: NotificationRequest, callback: AsyncCallback): void发布通知。
cancel(id: number, label: string, callback: AsyncCallback): void取消指定的通知。
cancelAll(callback: AsyncCallback): void;取消所有该应用发布的通知。

基础通知

应用可以通过通知接口发送通知消息,提醒用户关注应用中的变化。用户可以在通知栏查看和操作通知内容。

基础类型通知主要应用于发送短信息、提示信息、广告推送等,支持普通文本类型、长文本类型、多行文本类型和图片类型。

基础类型通知中的内容分类

类型描述
NOTIFICATION_CONTENT_BASIC_TEXT普通文本类型。
NOTIFICATION_CONTENT_LONG_TEXT长文本类型。
NOTIFICATION_CONTENT_MULTILINE多行文本类型。
NOTIFICATION_CONTENT_PICTURE图片类型。

开发步骤

  1. 导入NotificationManager 模块
import notificationManager from '@ohos.notificationManager';
  1. 构造NotificationRequest对象,并发布通知。

1. 构建并发送普通文本通知

普通文本类型的通知由标题title、文本内容text和附加信息additionalText三个字段组成,其中标题和文本内容是必填字段。

//构建通知请求
let notificationRequest = {
  id: 1,
  content: {
    contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知
    normal: {
      title: 'test_title',//标题
      text: 'test_text',//文本内容
      additionalText: 'test_additionalText',//附加信息
    }
  }
}
//发送通知
NotificationManager.publish(notificationRequest, (err) => {
    if (err) {
        console.error(`[ANS] failed to publish, error[${err}]`);
        return;
    }
    console.info(`[ANS] publish success`);
});

运行效果如下:

2. 构建并发送长文本通知

长文本通知多行文本通知在使用上其实只需要修改构建请求时的contentType类型,并补充其他用到的参数就可以了。
长文本类型通知继承了普通文本类型的字段,同时新增了长文本内容、内容概要和通知展开时的标题。通知默认显示与普通文本相同,展开后,标题显示为展开后标题内容,内容为长文本内容。所以我们在定义时,需要定义两套文本,一套展开前,一套展开后。

let notificationRequest = {
  id: 1,
  content: {
    contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT, // 长文本类型通知
    longText: {
      title: 'test_title',//默认展开前标题。
      text: 'test_text',//展开前内容
      additionalText: 'test_additionalText',//附加信息
      longText: 'test_longText',//展开后长文本
      briefText: 'test_briefText',//内容概要
      expandedTitle: 'test_expandedTitle',//展开后长标题
    }
  }
}

// 发布通知
NotificationManager.publish(notificationRequest, (err) => {
    if (err) {
        console.error(`[ANS] failed to publish, error[${err}]`);
        return;
    }
    console.info(`[ANS] publish success`);
});

运行效果如下图所示:

3. 构建并发送多行文本通知

多行文本类型通知继承了普通文本类型的字段,同时新增了多行文本内容、内容概要和通知展开时的标题。
通知默认显示与普通文本相同,展开后,标题显示为展开后标题内容,多行文本内容多行显示。

//构建通知请求
let notificationRequest = {
  id: 1,
  content: {
    contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_MULTILINE, // 多行文本类型通知
    multiLine: {
      title: 'test_title',//默认标题
      text: 'test_text',//默认文本
      briefText: 'test_briefText',//内容概要
      longTitle: 'test_longTitle',//展开后标题
      lines: ['line_01', 'line_02', 'line_03', 'line_04'],//展开后文本,每个元素代表一行内容。
    }
  }
}

运行效果如下图所示:

4. 构建并发送图片通知

图片类型通知继承了普通文本类型的字段,同时新增了图片内容、内容概要和通知展开时的标题,图片内容为PixelMap型对象,其 大小不能超过2M

//创建PixelMap
let rm = getContext(this).resourceManager
//先获取图片。
rm.getMediaContent($r('app.media.icon'), (error, result) => {
  console.log("console");
  if (!error) {
    image.createImageSource(result.buffer).createPixelMap()
      .then(picture => {
        //getPixelBytesNumber():获取图像像素图的总字节数
        console.log(picture.getPixelBytesNumber().toString());
        let notificationRequest: NotificationManager.NotificationRequest = {
          id: 1,
          content: {
            contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE,
            picture: {
              title: 'test_title',//通知标题
              text: 'test_text',//通知内容
              additionalText: 'test_additionalText',//通知附加内容,是对通知内容的补充。
              briefText: 'test_briefText',//通知概要内容,是对通知内容的总结。
              expandedTitle: 'test_expandedTitle',//通知展开时的标题。
              picture: picture	//通知的图片内容。
            }
          }
        };
        //发送通知
        NotificationManager.publish(notificationRequest, (err) => {
          if (err) {
            console.error(`[ANS] failed to publish, error[${JSON.stringify(err)}]`);
            return;
          }
          console.info(`[ANS] publish success`);
        });
      })
      .catch(reason => console.log("失败原因:" + JSON.stringify(reason)))
  }else {
    console.log("获取imageSource失败,失败原因:"+JSON.stringify(error))
  }
})

异常处理:

如果publish图文通知后,返回了错误信息 Invalid parameter err_code:401,那么先检查参数有没有缺少,再检查是不是PixelMap太大了,换个小的图片试试。因为我用这个图片就一直报错401,参数错误。

5. 附言

这一篇我们主要学习了四种基础通知的使用,但NotificationRequest还有很多的公用属性可以设置:

比如 delive(设置显示的通知时间),showDeliveryTime(是否显示通知时间,默认不显示),groupName(通知属于哪个组),slotType(消息通道)。

slotType的参数是个枚举类,它有以下类型,我们可以根据需要的效果不同来选择对应的消息通道。

类型枚举说明状态栏图标提示音横幅
SOCIAL_COMMUNICATION社交类型
SERVICE_INFORMATION服务类型×
CONTENT_INFORMATION内容类型××
OTHER_TYPES其他×××

进度条通知

进度条通知也是常见的通知类型,它会展示一个动态的进度条,主要应用于文件下载、长任务处理进度显示。

  1. 导入通知所需的包,和基础类型通知是同一个包:import NotificationManager from '@ohos.notificationManager';
  2. 首先我们要判断当前系统是否支持进度条通知模板;通过使用isSupportTemplate()来查询是否支持对应模板,目前仅支持进度条模板(只要是带进度的通知,我们都可以用这个模板,不一定非得是下载任务)。
//发送进度图类通知
async sendNotificationProgress(){
  //判断当前系统是否支持 "downloadTemplate" 下载进度模板通知
  let isSupport = await NotificationManager.isSupportTemplate("downloadTemplate")
  if (!isSupport) {
    //当前系统不支持下载进度通知模板
    return
  }
  //支持,创建下载进度通知
}
  1. 构造进度条模板对象,并发布通知。
//构建下载进度条通知模板
let template = {
  name: 'downloadTemplate', //模板名称,必须是downloadTemplate
  data: {
    title: '标题:', //在此处定义 文件下载任务的标题
    fileName: 'music.mp4', //在此处定义 文件的名字
    progressValue: 30, //进度条当前进度
    progressMaxValue: 100, //进度条总进度
  }
}

//构造NotificationRequest对象
let notificationRquest: NotificationManager.NotificationRequest = {
  id: 999,
  slotType: NotificationManager.SlotType.OTHER_TYPES,
  template: template,
  content: {
    contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, //其他类型的通知
    normal: {
      title: template.data.title + template.data.fileName, //通知的标题:把temp对象中定义的文本拼接后作为通知标题。
      text: "sendTemplate", //通知文本,不会显示
      additionalText: "30%" //附加内容
    }
  },
  deliveryTime: new Date().getTime(), //通知的时间
  showDeliveryTime: true //显示通知时间
}
NotificationManager.publish(notificationRquest).then(() => {
  console.info(`[ANS] publish success `);
}).catch((err) => {
  console.error(`[ANS] failed to publish, error[${err}]`);
});

注意:通知中的属性即使是用了 @State 变量,但是该属性变化时,通知是不会变化的。所以每次的属性状态变更时我们都需要重新构建通知并发布才能覆盖刷新(通知ID要一致才可覆盖)。

为通知添加行为意图

我们可以给通知或其中的按钮设置行为意图,从而实现拉起应用组件发布公共事件等能力。

运行机制

携带行为意图的通知运行机制
发布通知的应用 向 应用组件管理服务AMS(Ability Manager Service)申请WantAgent,然后随其他通知信息一起发送给桌面,当用户在桌面通知栏上点击通知时,触发WantAgent动作。

使用步骤

  1. 导入模块:
    这里用到两个模块;wantAgent行为意图模块,以及通知模块。
import NotificationManager from '@ohos.notificationManager';
import wantAgent from '@ohos.app.ability.wantAgent';
  1. 创建意图行为信息WantAgentInfo,这里涉及到两种场景
    1. 场景一:拉起Ability
      wants是目标组件信息:它是一个数组类型,它可以定义多个要被拉起的Ability信息。
      operationType是意图的类型:它的参数是枚举,如START_ABILITY 调起一个AbilitySTART_ABILITIES 调起多个AbilitySTART_SERVICE 开启服务SEND_COMMON_EVENT 发布公共事件
      wantAgentFlags:行为意图的标识;CONSTANT_FLAG表示当前的wantAgent行为意图是不可变的
let wantAgentObj = null; // 用于保存创建成功的wantAgent对象,后续使用其完成触发的动作。

async sendWantAgentNotification(){
  // 通过WantAgentInfo的operationType设置动作类型。
  let wantAgentInfo = {
      wants: [
          {
              deviceId: '',//设备ID,可以不填。
              bundleName: 'com.example.test',//目标Ability所在的包名
              abilityName: 'EnrtyAbility',//Ability的名称
              action: '',
              entities: [],
              uri: '',
              parameters: {}
          }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,//意图类型
      requestCode: 0,//请求码
      wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]//标识,CONSTANT_FLAG表示当前的wantAgent行为意图是不可变的。
  }
}
    1. 场景二:发布公共事件
let wantAgentObj = null; // 用于保存创建成功的WantAgent对象,后续使用其完成触发的动作。

async sendWantAgentNotification(){
  // 通过WantAgentInfo的operationType设置动作类型。
  let wantAgentInfo = {
      wants: [
          {
              action: 'event_name', // 设置事件名。
              parameters: {},
          }
      ],
      operationType: wantAgent.OperationType.SEND_COMMON_EVENT,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG],
  }
}
  1. 创建wantAgent实例:
//创建wantAgent实例
this.wantAgentObj = await wantAgent.getWantAgent(wantAgentInfo)
  1. 构造 NotificationRequest通知请求 对象。
//构建 notificationRequest 通知请求对象
let notificationRequest = {
  id: 1,
  label:'testlabel',
  wantAgent:this.wantAgentObj,
  content: {
    contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知
    normal: {
      title: '点我打开EntryAbility',//标题
      text: '点我吧',//文本内容
      additionalText: 'test_additionalText',//附加信息
    },
  }
}
  1. 发布 带有wantAgent 行为意图的通知
//发送通知
NotificationManager.publish(notificationRequest, (err) => {
  if (err) {
    console.error(`[ANS] failed to publish, error[${err}]`);
    return;
  }
  console.info(`[ANS] publish success`);
});
  1. 用户通过点击通知栏上的通知,即可触发WantAgent的动作。

公共广播事件

前面我们讲到了带行为意图的通知,它除了能用来调起Ability外,还可以发起公共事件,那我们接下来学习下公共广播事件的接收处理。

  1. 首先创建公共广播类型的意图
// 通过WantAgentInfo的operationType设置动作类型。
let wantAgentInfo = {
  wants: [
    {
      action: 'common_event_test2024', // 设置事件名称。
      parameters: {},
    }
  ],
  operationType: wantAgent.OperationType.SEND_COMMON_EVENT,
  requestCode: 0,
  wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG],
}
  1. 我们使用commonEventManager.createSubscriber创建订阅者、CommonEventManager.subscribe添加订阅者来实现公共事件订阅。
    1. commonEventManager.createSubscriber
createSubscriber(subscribeInfo: CommonEventSubscribeInfo): Promise<CommonEventSubscriber>
创建订阅者(Promise形式)。
参数名类型必填说明
subscribeInfoCommonEventSubscribeInfo表示订阅信息。
    1. CommonEventManager.subscribe
subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback<CommonEventData>): void
订阅公共事件(callback形式)。
参数名类型必填说明
subscriberCommonEventSubscriber表示订阅者对象。
callbackAsyncCallback<CommonEventData>表示接收公共事件数据的回调函数。
  1. 首先使用commonEventManager.createSubscriber来创建订阅者。
    第一个参数是 {events:['common_event_test2024']} 对象,events存储了需要订阅的事件名称 (我们创建 wantsInfo 时的action字段就是事件名称) ;events是个数组,表明可以订阅多个事件。
    第二个参数是创建订阅者的状态回调,创建成功后会将订阅者对象返回,我们接下来的操作都是要用到 订阅者对象 commonEventSubscriber的;所以建议把它存为成员变量,方便后面的事件取消订阅。
commonEventManager.createSubscriber({events:['common_event_test2024']},(err,commonEventSubscriber)=>{
  if (!err) {
    //得到创建的订阅者
    
  }
})
  1. 获取订阅者对象后,调用 CommonEventManager.subscribe ,第一个参数就将我们创建的订阅者对象填入,第二个参数是函数回调,这里会将我们订阅的公共事件从此处实时返回。为公共事件添加订阅者:
commonEventManager.subscribe(commonEventSubscriber,(err,data)=>{
              if (!err) {
                //收到公共广播事件
                this.message="收到消息:"+JSON.stringify(data)
                console.log(this.message);
              }
            })
  1. 取消订阅,当不需要再订阅时,需要及时取消,使用CommonEventManager.unsubscribe
    第一个参数传入我们的订阅者对象,第二个参数是取消订阅状态的回调。
commonEventManager.unsubscribe(this.commonEventSubscriber,(err)=>{
  if (!err) {
    console.log("取消订阅成功");
  }
})

效果演示:

总结

  • 我们学习了如何 订阅公共事件、以及公共事件的取消使用。
  • 除此之外公共事件还支持接收系统公共事件,当然这需要特定的权限声明。
  • 其次commonEventManager自然也是支持公共事件的发布的,这一块可以自行去学习。
    @ohos.commonEventManager (公共事件模块)官方文档