Skip to content

Android休眠机制

clarkehe edited this page Sep 21, 2016 · 32 revisions

在上一篇中讲到因为手机休眠而引发一些问题。android手机为什么要休眠呢?原因很简单:省电。

现在手机电池的容量有了一定提升,但容量还是有限的,好多智能机型都不支持换电池。在有限的电量前提下,智能机功能强,应用多,电量消耗就是一个问题。至少能做一天一充吧,不然android智能机用起来,电池就是一个很大的硬伤。

Android休眠机制

为了解决在有限电池容量条件下,尽可能提升电池的续航能力,android系统引休眠机制。PC和MAC笔记本也有这个机制,相对而言,android系统的休眠机制更复杂些。android内核基于Linux,休眠机制也是基于Linux的,但有一些不同。

android framework linux sleep

Linux系统的休眠主要包括三个主要的步骤:

  • 冻结用户态进程和内核态任务;
  • 调用注册的设备的suspend的回调函数;
  • 按照注册顺序休眠核心设备和使CPU进入休眠态。

进入休眠的原因:硬件中断,设置/sys/power/state为standby。用户也可以通过读写文件/sys/power/state实现控制系统进入休眠系统,从而长时间处于idle状态。比如:

  • echo standby > /sys/power/state 命令系统进入休眠
  • cat /sys/power/state 来得到内核支持哪几种休眠方式

冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文。所有的进程都还在,只在进入了supspend的状态,可以通过命令行参数suspend或恢复某个进程。

在oppo手机上就曾发现,如果设置手机为省电模式,系统会将后台app的所有进程全部supspend,虽然手机还没有进入休眠状态。如果手机已经root,可通过命令行对suspend的进程进行(唤醒)恢复。

Android略有不同,引入了Early suspend的概念。这个机制定义了在suspend的早期,关闭显示屏的时候,一些和显示屏相关的设备,比如背光、重力感应器和触摸屏等设备都应该被关掉。但是此时系统可能还有持有wake lock的任务在运行,如音乐播放,或者扫描sd卡上的文件等,这个时候整个系统还不能进入真正休眠,直到所有的wake lock都没释放。Early suspend是在熄屏后,提前将一些不会用到的设备关闭。

与Early suspend相对应,Android还有一个Late resume的概念。Late resume是指Android退出休眠后,唤醒在Early suspend关闭的设备。唤醒与休眠的顺序是相反的。

Android系统进入休眠的过程大概如下:

  • 系统开机后进入Active状态,此时系统开始计时。当不操作手机的休眠计时时间到了或者用户按下了电源键,系统会进入Early suspend状态。如果在Early suspend状态有任何用户activity发生,比如按键、充电等,将重置系统计数器,同时重新进入Active状态。
  • 如果在Early suspend状态系统判断没有partial wake lock,则进入真正的suspend状态,即sleep状态。此时,系统CPU进入休眠(深度休眠)。
  • 系统处于sleep状态时,如果检测到任何一个可以唤醒系统的条件(如按下电源键、开始充电、alarm、来电、短信等),则CPU会从sleep中唤醒,并回到Active状态(也有可能回到Early suspend状态)。

sleep wake

Wake Lock

Wake Lock是android电源管理中很重要的机制。它是一种锁的机制, 只要有任务拿着这个锁, 系统就无法进入休眠, 可以被用户态进程和内核线程获得。这个锁可以是有超时的或者是没有超时的, 超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了, 内核就会启动标准linux的那套休眠机制机制来进入休眠。

只要有一个Wake Lock是active的,系统是无法休眠的,而android的app可以申请这样的Wake Lock,app有bug了,系统就无法休眠。app获取wake lock的代码如下:

PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wakeLock.acquire();

//别忘了在操作完毕之后释放掉
if (wakeLock != null) {
   wakeLock.release();
   wakeLock = null;
}

Wake Lock有几种不同的类型,各种锁的类型对CPU 、屏幕、键盘的影响不同:
(1)PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的;
(2)SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯;
(3)SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯;
(4)FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度。

有人用图总结了不同电源状态(电源状态就是对应上面四种不同类型的wake lock)条件下,设备的状态及休眠过程。

wake type wake type and sleep

AlarmManager

说到休眠,就必须讲到AlarmManager。系统在进入休眠后,进程suspend,CPU进入休眠状态。可能有些任务或事情在系统休眠后也要能执行或完成,如闹钟,wx要能在后台正常接收消息等,这就就要借助alarm机制。

系统在休眠后,只有系统时钟RTC(Real Time Clock)在进行工作,RTC是一个独立的硬件时钟,可以在CPU休眠时正常运行,在预设的时间到达时,通过中断唤醒CPU。AlarmManager是Android系统封装的用于管理RTC的模块。AlarmManager有两种形式:

  • 在指定的时刻到来时执行某项操作。如你已经设定了一个闹钟时间,当该时间到来时,AlarmManager就为我们广播一个已设定的Intent,通过这个intent来执行一些操作。
  • 以指定的时间间隔周期性的执行某项操作。

使用alarm,使用系统接供的接口就可以了,可以参考官方文档

    public void set(int type, long triggerAtMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null);
    }

    public void setRepeating(int type, long triggerAtMillis,
            long intervalMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation, null,
                null);
    }

使用alarm要注意2个问题:

  • alarm会唤醒CPU,频繁的操作也会耗电。
  • alarm的时间精度。由于每个app都能独立设置alarm,4.4+后系统为了避免alarm频繁唤醒CPU,会将alarm集中处理(对齐),这也影响了alram的时间精度。如果要设置时间精确的alarm,可参考文档。
Clone this wiki locally