两个处理器

Application Processor (AP):

AP是ARM架构的处理器,用于运行Linux+Android系统。

Baseband Processpr (BP):

BP用于运行实时操作系统(RTOS),运行手机射频通信控制软件。

非通话时间BP能耗很低;而AP由于需要运行操作系统、用户界面和应用程序,只要处于非休眠状态能耗相对BP要高出很多,执行图形运算会更高。

让系统保持“清醒”

当手机灭屏状态下保持一段时间后,系统会进入休眠,一些后台任务比如网络下载,播放音乐会得不到正常的执行。WakeLock API可以确保应用程序中关键代码的正确执行,使应用程序有能力控制AP的休眠状态。

一种锁机制

当应用申请了WakeLock,WakeLock会阻止AP挂起,系统无法进入休眠,即使在灭屏的状态下,应用要执行的任务依然不会被打断。当所有WakeLock被释放(解锁/超时),系统会挂起启动休眠机制进入休眠。

使用WakeLock

<!--WakeLock需要的权限-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
PowerManager pm= (PowerManager) this.getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock=pm.newWakeLock(PowerManager.ON_AFTER_RELEASE|PowerManager.PARTIAL_WAKE_LOCK,"Tag");
 //申请WakeLock
 wakeLock.acquire();

 //... do work...

 //释放wakeLock
 //当使用wakeLock.acquire(timeout)的方式时系统会自动释放
 wakeLock.release();

WakeLock的分类和Flag

分类

  • PARTIAL_WAKE_LOCK: 灭屏,关闭键盘背光的情况下,CPU依然保持运行。
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK: 基于距离感应器熄灭屏幕。最典型的运用场景是我们贴近耳朵打电话时,屏幕会自动熄灭。
  • SCREEN_DIM_WAKE_LOCK
    SCREEN_BRIGHT_WAKE_LOCK
    FULL_WAKE_LOCK
    三种过时方法,它们的目的是保持屏幕常亮。
    官方建议使用:
//不需要申请WakeLock和权限
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  • DOZE_WAKE_LOCK/DRAW_WAKE_LOCK: 隐藏的分类,系统级别才会用到。

Flag

  • ACQUIRE_CAUSES_WAKEUP: 点亮屏幕,比如应用接收到通知后,屏幕亮起。
  • ON_AFTER_RELEASE: 释放WakeLock后,屏幕不马上熄灭。
  • UNIMPORTANT_FOR_LOGGING: 隐藏的flag,系统级别才会用到。

其他方法

//判断是否已经获取WakeLock
 wakeLock.isHeld();
//是否引用计数器
 wakeLock.setReferenceCounted(false);

默认true开启引用计数器,如果一个WakeLock acquire了多次也必须release多次才能释放掉。但是如果释放次数比acquire多则会抛出RuntimeException(Tag)异常。
false关闭引用计数器时,无论 acquire() 了多少次,只要通过一次 release()即可解锁。

PS:PowerManager.WakeLock 的计数机制并只是对同一把锁被申请/释放的次数进行了统计再正去操作。

简单的debug方法

测试代码

/**
 * @Author: 吃茶泡饭丶
 * @CreateDate: 2018/9/3
 */
public class WakeLockActivity extends BaseActivity {

    private PowerManager.WakeLock wakeLock;

    @Override
    protected void onResume() {
        PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.ON_AFTER_RELEASE
                | PowerManager.PARTIAL_WAKE_LOCK, "Tag");
        //申请WakeLock
        wakeLock.acquire();
        super.onResume();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wake_lock);
        //... do work...
    }

    @Override
    protected void onPause() {
        //释放wakeLock
        //当使用wakeLock.acquire(timeout)的方式时系统会自动释放
        wakeLock.release();
        super.onPause();
    }
}

通过adb 命令

adb shell dumpsys "power|grep -i wake" 
and
$ adb shell dumpsys power|grep -i wake

进入onResume申请WakeLock,dumpsys的信息

no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x1
  mLastWakeTime=509144080 (30113 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDoubleTapWakeEnabled=false
  Wake Locks: size=1
  mLock:176961948 PARTIAL_WAKE_LOCK              'Tag' ON_AFTER_RELEASE ACQ=-2s581ms (uid=10155 pid=26429)
  PowerManagerService.WakeLocks: ref count=1
  Proxyed WakeLocks State

进入onPause释放WakeLock,dumpsys的信息

no_cached_wake_locks=true
  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x0
  mLastWakeTime=509144080 (271089 ms ago)
  mHoldingWakeLockSuspendBlocker=false
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDoubleTapWakeEnabled=false
  Wake Locks: size=0
  PowerManagerService.WakeLocks: ref count=0
  Proxyed WakeLocks State

准确的debug方法

使用battery historian 工具,在应用运行一段时间后抓取bugreport查看wakelock申请情况。
https://github.com/google/battery-historian 需要搭建GO语言环境