文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

OpenHarmony 源码解析之SystemUi—Statusbar

2024-12-02 07:50

关注

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​

简介

SystemUI应用是OpenHarmony中预置的系统应用,为用户提供系统相关信息展示及交互界面,包括系统状态、系统提示、系统提醒等,例如系统时间、电量信息。

源码地址:https://gitee.com/openharmony/applications_systemui/tree/master

本文主要分析batterycomponent、clockcomponent、wificomponent三大组件:

  1. 导入batteryInfo模块,监听系统电池事件,实时获取电池电量状态
  2. 导入时间模块,调用JS内置函数,实时获取系统时间、日期、星期信息
  3. 导入Wifi模块,监听设备Wlan通信与连接事件,实时获取Wifi开关、供电、连接名等信息

发布-订阅模式

源码中灵活使用了发布-订阅模式(发布-订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知)来对battery和wifi事件进行监听,当页面初始化时定义订阅事件,页面取消初始化时定义退订事件,由此,当页面初始化时,即执行对事件的订阅与监听,页面初始化完成时,订阅监听的逻辑处理已完成并返回相应的结果。

在本文中以battery和wifi组件为例,将要监听的事件放在数组中,创建对该数组内事件的订阅来执行监听,订阅回调中设置该数组为data,监听到事件发生,触发事件回调,传入err和data参数,当err.code为0时(表示函数正常运行),对data进行判断,监听到对应的事件(即监听到battery或者wifi状态发生了变化),再通过调用相应模块下的方法来获取所需的数值,这样一来,电池和wifi的状态信息一有变化,便重新获取一次,就实现了对电池和wifi状态信息的实时获取。

相较于常规的回调函数,发布-订阅模式存在以下的优缺点:

优点:发布者与订阅者耦合性降低,发布者只管发布一条消息出去,它不关心这条消息如何被订阅者使用,同时,订阅者只监听发布者的事件名,只要发布者的事件名不变,它不管发布者如何改变。

缺点:1.创建订阅者需要消耗一定的时间和内存。

2.虽然可以弱化对象之间的联系,如果过度使用的话,反而使代码不好理解及代码不好维护等等。

架构图

目录

   /applications/standard/systemui
├── build.gradle # 全局编译配置文件
├── settings.gradle # 编译模块配置文件
├── LICENSE # 许可文件
├── common # 通用工具类目录
├── entry # entry模块目录
├── signature # 证书文件目录
├── features # 子组件目录
├── batterycomponent # 电池组件
├── clockcomponent # 时间组件
├── control # 控制中心组件
├── navigationservice # 导航栏服务组件
├── noticeitem # 通知子组件
├── notificationservice # 通知服务组件
├── signalcomponent # sim卡信号组件
├── wificomponent # wifi组件
├── product # SystemUI总体功能目录
├── navigationBar # 导航栏模块目录
├── statusbar # 状态栏模块目录
├── systemDialog # 系统弹框模块目录

batterycomponent

简介

电池组件只需监听‘usual.event.BATTERY_CHANGED’即电池变化事件即可,监听到电池变化,便获取一次电池的状态信息,实时获取电池的状态信息(是否在充电,电量百分比),以实现以下功能:

1.自动在充电时展示更醒目的图标,比如填充绿色、带闪电等

2.动态展示电量百分比变化

官方接口文档

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-battery-info-0000001100730486

源码地址

https://gitee.com/nicklaus0602/applications_systemui/tree/master/features/batterycomponent

导入模块

import BatteryInfo from '@ohos.batteryInfo';      
import BatterySubscriber from '@ohos.commonEvent';

申明变量与初始化

var mBatterySoc;
var mBatteryCharging;

let mProgress = Constants.DEFAULT_PROGRESS;
let mBatteryEventSubscriber = null;
let mBatteryEventSubscribeInfo = {
events: ['usual.event.BATTERY_CHANGED']
}

export class BatteryStatus {

initBatteryStatus() {
Log.showInfo(TAG, 'initBatteryStatus');
mBatterySoc = AppStorage.SetAndLink('batterySoc', 0);
mBatteryCharging = AppStorage.SetAndLink('batteryCharging', false);
if (mBatteryEventSubscriber == null) {
this.registerBatteryListener();
}
this.getBatteryStatus();
}

uninitBatteryStatus() {
Log.showInfo(TAG, 'uninitBatteryModel');
this.unregisterBatteryListener();
}

}

订阅与回调


private registerBatteryListener() {
Log.showInfo(TAG, 'registerBatteryListener start');
BatterySubscriber.createSubscriber(
mBatteryEventSubscribeInfo,
this.createBatterySubscriberCallBack.bind(this)
);
}


private unregisterBatteryListener() {
Log.showInfo(TAG, 'unregisterBatteryListener');
BatterySubscriber.unsubscribe(mBatteryEventSubscriber, () => {
Log.showInfo(TAG, `unregister Battery Status Listener ===============`);
});
}



private createBatterySubscriberCallBack(err, data) {
Log.showInfo(TAG, `Subscriberregister createBatterySubscriberCallBack err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
mBatteryEventSubscriber = data;
BatterySubscriber.subscribe(mBatteryEventSubscriber, this.batterySubscriberCallBack.bind(this));
}




private batterySubscriberCallBack(err, data) {
Log.showInfo(TAG, `batterySubscriberCallBack err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
if (err.code == 0) {
if (data.event == 'usual.event.BATTERY_CHANGED') {
this.getBatteryStatus();
}
} else {
Log.showInfo(TAG, 'Subscriberregister error when subscribing ========');
}
}

获取电池状态、电量


private getBatteryStatus() {
Log.showInfo(TAG,'getBatteryStatus')

// 调用BatteryInfo.batterySOC获取电量值
let batterySoc = BatteryInfo.batterySOC;

// 调用BatteryInfo.chargingStatus获取充电状态枚举值
let batteryCharging = BatteryInfo.chargingStatus;

if (null == batterySoc) {
// Set the battery Soc as full when there is no battery hardware 设备无电池硬件时,电量默认为满电
batterySoc = mProgress;
}
if (batterySoc <= 0) {
// 确保电量值不为负数
batterySoc = Math.abs(batterySoc) * Constants.PERCENT_NUMBER;
}

this.checkBatteryStatus(batteryCharging, (result) => {
let batteryStatus = result;
// 检查电池的充电状态 将电量的值赋给mBatterySoc,
mBatterySoc.set(batterySoc);
// 电池状态赋给mBatteryCharging
// 0是false,1、2、3是true
mBatteryCharging.set(batteryStatus);
});
}


private checkBatteryStatus(charging, callback) {
Log.showInfo(TAG, `checkBatteryStatus charging: ${charging}`);
let batteryStatus;
switch (charging) {
case Constants.NONE:
batteryStatus = false;
break;
case Constants.DISABLE:
case Constants.ENABLE:
case Constants.FULL:
batteryStatus = true;
break;
default:
batteryStatus = false;
break;
}
callback(batteryStatus);
}

Wificomponent

注:官方暂无WifiInfo模块的接口文档,只有“import wifi from ‘@ohos.wifi’”模块的相关文档,二者部分属性的枚举值相同,可以稍做参考。

wifi 模块地址:https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-wlan-0000001121342036

简介

wifi模块需要监听的事件较多,需要监听以下事件:

1.连接事件’usual.event.wifi.CONN_STATE’,

2.供电事件’usual.event.wifi.POWER_STATE’(常用于路由器等设备)

3.连接名事件’WIFI_CONNECT_NAME’,

通过监听wifi的连接状态变化来判断wifi的开关状态,通过监听wifi的供电状态变化来判断wifi是否可用,通过监听wifi连接的名称变化来判断当前连接的是哪一个wifi网络。

通过对这三个事件的监听,我们可以实时获取wifi的开关状态,供电状态,连接名称,借此可以实现以下功能:

1.自动根据wifi开关状态设置不同图标

2.自动展示连接的wifi名称

源码地址

https://gitee.com/openharmony/applications_systemui/tree/master/features/wificomponent

导入模块

import WifiInfo from '@ohos.wifi_native_js';
import Subscriber from '@ohos.commonEvent';

申明变量与初始化

var mCommonEventSubscribeInfo = {
events: [Constants.EVENT_CONN_STATE,
Constants.EVENT_POWER_STATE, Constants.Event_CONN_NAME]
};

var mCommonEventSubscriber = null;
var mWifiInfo;
var mWifiStatus;
var mWifiOpenStatus;

export class WifiModel {

initWifiModel() {
Log.showInfo(TAG, `initWifiModel`)
mWifiInfo = AppStorage.SetAndLink("wifiInfo", 0);
mWifiStatus = AppStorage.SetAndLink("wifiStatus", false);
mWifiOpenStatus = AppStorage.SetAndLink("wifiOpenStatus", false);
if(mCommonEventSubscriber == null){
this.registerWiFiStatusListener();
}
this.getWifiMessage();
}


uninitWifiModel() {
Log.showInfo(TAG, `uninitWifiModel`)
this.unregisterWiFiStatusListener()
}
}

订阅与回调

通过创建对wifi事件的订阅,来监听mCommonEventSubscribeInfo中的三个事件,‘usual.event.wifi.CONN_STATE’,‘usual.event.wifi.POWER_STATE’,‘WIFI_CONNECT_NAME’,若事件发生,则触发回调,


registerWiFiStatusListener() : void {
Log.showInfo(TAG, `register Wifi status listener ===========`);
Subscriber.createSubscriber(
mCommonEventSubscribeInfo,
this.createWifiStatusSubscriberCallBack.bind(this)
);
}


createWifiStatusSubscriberCallBack(err, data) {
Log.showInfo(TAG, `createWifiStatusSubscriberCallBack start err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
mCommonEventSubscriber = data;
Subscriber.subscribe(mCommonEventSubscriber, this.wifiStatusSubscriberCallBack.bind(this));
}


private wifiStatusSubscriberCallBack(err, data) {
Log.showInfo(TAG, `wifiStatusSubscriberCallBack start err:${ JSON.stringify(err)} data: ${ JSON.stringify(data) }`);
// 默认枚举值 DEFAULT_ERR_CODE: number = 0,代表函数正常运行
if (err.code == Constants.DEFAULT_ERR_CODE) {
Log.showInfo(TAG, `wifi data == ${JSON.stringify(data)}`);
// 枚举值 EVENT_CONN_STATE: string = 'usual.event.wifi.CONN_STATE',表示wifi连接状态变化
if (data.event == Constants.EVENT_CONN_STATE) {

if(data.code == Constants.WIFI_STATE_AP_CONNECTING ||
data.code == Constants.WIFI_STATE_NETWORK_ENABLED ||
data.code == Constants.WIFI_STATE_NO_NETWORK){
this.updateWifiInfo();
this.changeWifiStatus(true);
}else {
this.updateWifiInfo();
this.changeWifiStatus(false);
mWifiName.set('WLAN');
}
}
// WIFI_POWER_OFF: number = 0; WIFI_POWER_ON: number = 4;
// data.code == 0,wifi不可用,mWifiOpenStatus.set(false),
// data.code == 4,wifi可用,mWifiOpenStatus.set(true)

// 枚举值 EVENT_POWER_STATE: string = 'usual.event.wifi.POWER_STATE',表示wifi供电状态变化(常用于路由器等设备)
if (data.event == Constants.EVENT_POWER_STATE) {
if( data.code == Constants.WIFI_POWER_OFF){
this.updateWifiInfo();
this.changeWifiStatus(false);
mWifiOpenStatus.set(false);
mWifiName.set('WLAN');
}else if(data.code == Constants.WIFI_POWER_ON){
mWifiOpenStatus.set(true);
}
}
// 枚举值 Event_CONN_NAME: string = 'WIFI_CONNECT_NAME',表示连接名称变化,意味着连接到了wifi网络(或者切换到了其他的wifi网络),可以获取当前wifi的信号强度了,更新wifi名称,并设置wifiStatus为true,表示已连接
if(data.event == 'WIFI_CONNECT_NAME'){
this.updateWifiName(JSON.parse(data.data).name)
this.changeWifiStatus(true)

// rssi是热点的信号强度(dBm),类型为number,取值范围为[0, 4],rssi为4时wifi信号满格
// band是WLAN接入点的频段,类型为number
// 将rssi,band传入函数updateWifiInfo()
let rssi = JSON.parse(data.data).rssi
let band = JSON.parse(data.data).band
Log.showInfo(TAG, `WIFI_CONNECT_NAME ================ ${rssi}`)
this.updateWifiInfo(rssi,band);
}
} else {
Log.showError(TAG, `error when subscribing ========`);
}
}


private unregisterWiFiStatusListener() {
Subscriber.unsubscribe(mCommonEventSubscriber, () => {
Log.showInfo(TAG, `unregister Wifi Status Listener ===============`);
});
}

获取Wifi状态


private changeWifiStatus(status) {
Log.showInfo(TAG, `enter changeWifiStatus ================ ${status}`);
mWifiStatus.set(status)
}

// 更新wifi信息,传入参数rssi,band
private updateWifiInfo(rssi=Constants.UPDATE_WIFI_INFO_DEFAULT_PARAM,band=Constants.SIGN_LEVEL) {
Log.showInfo(TAG, `enter changeWifiInfos ================ ${rssi}+${band}`);
mWifiInfo.set(this.getWifiInfo(rssi,band))
Log.showInfo(TAG, `mWifiInfo wifiInfo ${mWifiInfo.get()}`);
}
// 更新wifi名称
private updateWifiName(name) {
Log.showInfo(TAG, `enter changeWifiNames ================ ${name}`);
mWifiName.set(name);
}



private getWifiInfo(rssi,band) {
//The current version keeps return 0 as result, set the level as 4(full level) by hand.
Log.showInfo(TAG, `getWifiImage enter =========`);
//Fake number of band and rssi for wifi signal level temporarily
let level = WifiInfo.getSignalLevel(rssi, band);
Log.showInfo(TAG, `wifi level = ${level}`);
return level;
}
// 启用wifi(暂不可用)
enableWifi() {
Log.showInfo(TAG, 'enableWifi ing');
WifiInfo.enableWifi();
}
// 关闭wifi(暂不可用)
disableWifi() {
Log.showInfo(TAG, 'disableWifi ing');
WifiInfo.disableWifi();
}

Clockcomponent

简介

对于系统时间的获取较为简单,只需在页面初始化时调用JS的内置函数来获取系统时间即可,需要注意的是因为要保持所展示的时间实时更新,需要使用以下函数来设置时间间隔,确保每隔一定的时间就调用一次函数,保证时间的实时更新。

setInterval(() => {
函数名;
}, 时间间隔);

复制本文中通过每隔3秒获取一次时间来展示系统时间,满足设备展示“xx:xx”即“小时-分钟“的需要,如果需要精度更高,就需要调整LOOP_TIME,使得调用函数的间隔更短。

源码地址

https://gitee.com/openharmony/applications_systemui/tree/master/features/clockcomponent

获取时间数据

const LOOP_TIME = 3000; 
var mTimeLink;
var mDayLink;
var mWeekDayLink;
var mMonthLink;

private getCurrentDate() {
Log.showInfo(TAG, 'getCurrentDate');
this.getDate();
timeInterval = setInterval(() => {
this.getDate();
}, LOOP_TIME);
}

// 实例化Date对象,获取系统当前时间,并作为参数传入JS内置函数获取想要的时分秒、日、周、月等值
private getDate(){
Log.showInfo(TAG, 'getDate');
let date = new Date();
this.updateTime(date);
this.updateDay(date);
this.updateWeekDay(date);
this.updateMonth(date);
}


private updateTime(date) {
let time = date.toTimeString().substring(0, 5);
mTimeLink.set(time);
}


private updateDay(date) {
let day = date.getDate();
mDayLink.set(day);
}


private updateWeekDay(date) {
let weekDay = date.getDay();
mWeekDayLink.set(weekDay);
}


private updateMonth(date) {
let month = (date.getMonth() + 1);
mMonthLink.set(month);
}

结语

本文对源码的方法进行了细致的解读,目标是节省广大开发者分析源码的时间,希望大家阅读本文后,可以将源码中从订阅监听事件到调用方法获得枚举值的整个模块直接拿来使用,以此助力鸿蒙OS生态搭建!

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​


来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯