Battery Status API提供了系统层级的电池信息(电量/充电信息等),并且在这些状态改变的时候提供了一系列的eventListener
。
有了这些信息,我们可以对应用进行优化。例如:
- 用户使用电池供电,想要达到好的续航效果,我们可以降低对资源的使用。
- 用户电量低,我们可以先对用户操作和数据进行缓存,避免数据丢失。
- 持续收集用户数据,进行用户群体分析。
- ……
兼容性 Battery Status API的支持度有限,目前只有Chrome和Opera以及Android webview支持度是比较好的,并且官方并不推荐使用该功能,未来或被移除。
navigator.getBattery
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 => { })
|
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
充电,翻翻上学时的记忆,无论是文档,代码,邮件…思绪回到六七年前,高中时代的自己,就是因为我对智能手机的狂热追求,我才选择了如今的职业。除此之外,还有一层不变的对游戏的热爱。我喜欢玩游戏,也曾想过做游戏,在上海的那一段日子,我畏畏缩缩的迈出过第一步(想法/剧本),后来也不了了之。
是时候重新出发,在未来的很长一段时间里,想要摸索着迈出第二步。
完。