Battery Status API 以及useBattery

Battery Status API提供了系统层级的电池信息(电量/充电信息等),并且在这些状态改变的时候提供了一系列的eventListener

有了这些信息,我们可以对应用进行优化。例如:

  • 用户使用电池供电,想要达到好的续航效果,我们可以降低对资源的使用。
  • 用户电量低,我们可以先对用户操作和数据进行缓存,避免数据丢失。
  • 持续收集用户数据,进行用户群体分析。
  • ……

兼容性 Battery Status API的支持度有限,目前只有ChromeOpera以及Android webview支持度是比较好的,并且官方并不推荐使用该功能,未来或被移除。

getBattery返回一个Promise对象,resolve后返回一个battery对象,该对象包含了{ charging, chargingTime, dischargingTime, level }分别表示是否在充电,充电时长,剩余可用时间,电池电量。例如,{ "charging": true, "level": 1, "chargingTime": 0, "dischargingTime": null }

除此之外,battery对象还包含了4个eventlistener(chargingchange/chargingtimechange/dischargingtimechange/levelchange),用于监听4个属性的改变。

下面来写一个例子,获取某一时刻的系统电量信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function getBattery() {
const nav = navigator
if (!nav || typeof nav.getBattery !== 'function') {
return {}
}
const battery = await navigator.getBattery()
console.log(battery.level)
return battery
}

getBattery()
.then(battery => {
// do something
})

useBattery

如果我们要在React中使用Battery Status API,我们仍然可以向上面一样,也可以配合上React hooks来实现一个useBattery hook。

在开始写这个钩子之前,我们先理一下,由于获取电量信息是一个异步的过程,所以这个钩子除了返回上面提到的4个电量信息属性以外,还需要额外的一个属性用于记录数据是否获取完毕。

判断浏览器兼容性只需要判断navigator对象是否包含getBattery函数即可

1
const isSupported = navigator && typeof navigator.getBattery === 'function'

获取某一时刻电量信息的hook如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useState, useEffect } from 'react'

const isSupported = navigator && typeof navigator.getBattery === 'function'

function useBattery() {
if(!isSupported) {
return {}
}
const [state, setState] = useState({ fetching: true })
useEffect(() => {
navigator.getBattery()
.then(battery => {
const newState = {
fetching: false,
charging: battery.charging,
level: battery.level,
dischargingTime: battery.dischargingTime,
chargingTime: battery.chargingTime,
}
setState(newState)
})
}, [])
return state
}

在某些情况下,我们可能并不仅仅需要某一时刻的电量信息,显然这一版本的useBattery并不能满足需要。我们需要监听eventListeners,并且在改变后变更状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { useState, useEffect } from 'react'

const isSupported = navigator && typeof navigator.getBattery === 'function'

import { isEqual } from 'lodash'

let bat;

function useBattery() {
if(!isSupported) {
return {}
}
const [state, setState] = useState({ fetching: true })
useEffect(() => {
function dealChange() {
const newState = {
fetching: false,
charging: battery.charging,
level: battery.level,
dischargingTime: battery.dischargingTime,
chargingTime: battery.chargingTime,
}
if (!isEqual(state, newState)) {
setState(newState)
}
}
navigator.getBattery()
.then(battery => {
bat = battery
dealChange()
bat.addEventListener('chargingchange', dealChange)
bat.addEventListener('chargingtimechange', dealChange)
bat.addEventListener('dischargingtimechange', dealChange)
bat.addEventListener('levelchange', dealChange)
return () => {
bat.removeEventListener('chargingchange', dealChange)
bat.removeEventListener('chargingtimechange', dealChange)
bat.removeEventListener('dischargingtimechange', dealChange)
bat.removeEventListener('levelchange', dealChange)
}
})
}, [])
return state
}

上面版本的useBattery已经比较完善了,针对每个属性都添加了eventHandler,当属性改变时获取新的state,并通过比较决定是否应用更改。

useEffect第一个参数如果返回一个函数,那么将在unmount的时候执行,所以,上面的代码进行了removeEventListener

尾巴

最近两周本该是上班的时间,由于疫情,我不得不在家待岗。起初的一周,睡睡懒觉, 看会儿NBA,再玩玩游戏,一天就浑浑噩噩的过了。可当游戏也玩得无聊了,懒觉也睡够了,我才发现我是真的没什么事做了,这种日子过得真的很难受!

今天给好久没打开过的Mac充电,翻翻上学时的记忆,无论是文档,代码,邮件…思绪回到六七年前,高中时代的自己,就是因为我对智能手机的狂热追求,我才选择了如今的职业。除此之外,还有一层不变的对游戏的热爱。我喜欢玩游戏,也曾想过做游戏,在上海的那一段日子,我畏畏缩缩的迈出过第一步(想法/剧本),后来也不了了之。

是时候重新出发,在未来的很长一段时间里,想要摸索着迈出第二步。

完。

分享到 评论