Android 功耗分析之wakelock

生活总是让我们遍体鳞伤,但到后来,那些受伤的地方一定会变成我们最强壮的地方。—海明威

  • WakeLock是什么
  • WakeLock作用
  • WakeLock有那些分类
  • WakeLock的设置过程
  • WakeLock用法
  • WakeLock相关问题的debug方法
    • 应用层debug
    • 系统层debug

WakeLock是什么

WakeLock是Android框架层提供的一套机制,应用使用该机制可以达到控制Android设备状态的目的。这里的设备状态主要指屏幕的打开关闭,cpu的保持运行。简单的理解WakeLock是让系统保持”清醒”的一种手段.

WakeLock作用

当手机灭屏状态下保持一段时间后,系统会进入休眠,一些后台运行的任务就可能得不到正常执行,比如网络下载中断,后台播放音乐暂停等。WakeLock正是为了解决这类问题,应用只要申请了WakeLock,那么在释放WakeLock之前,系统不会进入休眠,即使在灭屏的状态下,应用要执行的任务依旧不会被系统打断。

WakeLock有那些分类

WakeLock是PowerManager的内部类,其代码路径位于:

frameworks/base/core/java/android/os/PowerManager.java

WakeLock 分类如下:

  • PARTIAL_WAKE_LOCK: 灭屏,关闭键盘背光的情况下,CPU依然保持运行。
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK: 基于距离感应器熄灭屏幕。最典型的运用场景是我们贴近耳朵打电话时,屏幕会自动熄灭。
  • SCREEN_DIM_WAKE_LOCK/SCREEN_BRIGHT_WAKE_LOCK/FULL_WAKE_LOCK:这三种WakeLock都已经过时了,它们的目的是为了保持屏幕长亮,Android官方建议用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);方式替换。因为比起申请WakeLock,这种方式更简单,还不需要特别申请android.permission.WAKE_LOCK权限。
  • DOZE_WAKE_LOCK/DRAW_WAKE_LOCK: 隐藏的分类,系统级别才会用到。

WakeLock的flag如下:

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

WakeLock的设置过程

WakeLock从用户空间下发设置操作,然后进入kernel空间,最终写入到了/sys/power/wake_lock文件节点。 
下面来从源码的角度跟踪下acquire WakeLock的过程。

  1. frameworks/base/core/java/android/os/PowerManager.java 
    acquire—>acquireLocked—->PowerManagerService.acquireWakeLock

  2. frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 
    acquireWakeLock—>acquireWakeLockInternal—->updatePowerStateLocked—->updateSuspendBlockerLocked—->mWakeLockSuspendBlocker.acquire—->PowerManagerService$SuspendBlockerImpl.acquire—->nativeAcquireSuspendBlocker

  3. frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp 
    nativeAcquireSuspendBlocker—->acquire_wake_lock

  4. hardware/libhardware_legacy/power/power.c 
    acquire_wake_lock,最终在该方法里将wakelock写入了节点

    int acquire_wake_lock(int lock, const char* id)
    {initialize_fds();//    ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);if (g_error) return g_error;int fd;size_t len;ssize_t ret;if (lock != PARTIAL_WAKE_LOCK) {return -EINVAL;}fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];//这个节点就是/sys/power/wake_lockret = write(fd, id, strlen(id));if (ret < 0) {return -errno;}return ret;
    }
    • WakeLock用法

WakeLock的使用需要谨慎处理,使用不当会让应用变成“电量杀手”。使用的原则

  1. 能不用就尽量别用:比如要保持屏幕长亮,应该用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);的方式。

    PowerManager.FULL_WAKE_LOCK会保持屏幕长亮,比如设置15s自动灭屏,当申请了该wakelock后,即使超过15s,依然不会灭屏。但用户主动按power键,还是会灭屏的。

  2. 如果非要用,用完之后记得释放。 
    申请WakeLock有两种方式acquire()跟acquire(long timeout),后者相对更安全点,如果忘记了release WakeLock,经过timeout的时长后系统会自动release。

WakeLock的典型用法如下:

PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK| PowerManager.ON_AFTER_RELEASE,TAG);
wl.acquire();//为了保证任务不被系统休眠打断,申请WakeLock
// 开始我们的任务
wl.release();//任务结束后释放,如果不写该句。则可以用wl.acquire(timeout)的方式把释放的工作交给系统。
  • 同时需要在Manifest文件中添加权限
<uses-permission android:name="android.permission.WAKE_LOCK."/>
  • 1

WakeLock相关问题的debug方法

应用层debug

如果只是单纯的查看某一个应用的wakelock是否存在非正常释放的情况,可以用命令

$ adb shell dumpsys power|grep -i wake
  • 1

来查看,比如下面的示例代码

public class MainActivity extends Activity {private WakeLock wlLock;@Overrideprotected void onResume() {PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);wlLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "azhengye-test-wakelock");wlLock.acquire();super.onResume();}@Overrideprotected void onPause() {wlLock.release();super.onPause();}
}
  • 当进入onResume方法,此时dumpsys的信息如下:
$ adb shell dumpsys power|grep -i wakemWakefulness=AwakemWakefulnessChanging=falsemWakeLockSummary=0x1mLastWakeTime=34512600 (293075 ms ago)mHoldingWakeLockSuspendBlocker=truemWakeUpWhenPluggedOrUnpluggedConfig=truemWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=falsemDoubleTapWakeEnabled=false
Wake Locks: size=1PARTIAL_WAKE_LOCK              'azhengye-test-wakelock' ON_AFTER_RELEASE ACQ=-4m52s949ms LONG (uid=10124 pid=31473 pkg=com.azhengye.testpath)PowerManagerService.WakeLocks: ref count=1
  • 通过 PARTIAL_WAKE_LOCK 'azhengye-test-wakelock' ON_AFTER_RELEASE ACQ=-4m52s949ms LONG (uid=10124 pid=31473 pkg=com.azhengye.testpath) 可以看到wakelock申请成功了。

如果进入onPause方法,刚申请的wakelock应该被释放掉,此时dumpsys出的信息如下:

$ adb shell dumpsys power|grep -i wakemWakefulness=AwakemWakefulnessChanging=falsemWakeLockSummary=0x1mLastWakeTime=34512600 (477908 ms ago)mHoldingWakeLockSuspendBlocker=truemWakeUpWhenPluggedOrUnpluggedConfig=truemWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=falsemDoubleTapWakeEnabled=falseWake Locks: size=1PARTIAL_WAKE_LOCK              'AudioMix' ACQ=-2s539ms (uid=1041 pkg=audioserver)PowerManagerService.WakeLocks: ref count=1
  • 没毛病,已经正常释放掉。如果应用已经运行到释放wakelock的语句,但dumpsys出的信息仍然看到持有wakelock,这就是问题,需要我们去fix掉。

系统层debug

系统层面去debug wakelock相关问题就比较复杂了,基本上的步骤如下。

  1. 依据log分析 
    首先打开debug开关 
    frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private static final boolean DEBUG = true;
    • 然后dumpsys power查看,同时在logcat里搜索acquireWakeLockInternal关键字查看申请wakelock情况,

    比如

    08-23 09:45:35.452  3054  3067 D PowerManagerService: acquireWakeLockInternal: lock=1945552, flags=0x1, tag="LocationManagerService", ws=WorkSource{1000 android}, uid=1000, pid=3054, packageName=android
    • 搜索releaseWakeLockInternal查看释放情况。lock=后面的整数能将acquire跟release对应起来,在结合logcat的时间戳,就能得到wakelock持有的时长,短时间的一般不会有问题,那种长期持有wakelock的会导致功耗增加,一般是有问题的,需要根据tag字段去代码里查看具体的acquire/release过程。
  2. 系统运行一段时间后抓取bugreport.用historian工具查看wakelock申请情况。具体可以查看之前的博客Android battery historian功耗分析之环境搭建

Android 功耗(11)---Android 功耗分析之wakelock相关推荐

  1. Android 系统(11)---android 系统权限大全

    收集到的android权限都很实用的(permission)大全 1.android.permission.WRITE_USER_DICTIONARY 允许应用程序向用户词典中写入新词 2.andro ...

  2. alertdialog android api 11,android – 设备api级别11的DialogFragments

    我设法在兼容包的DialogFragment.java中正确修复了这个问题: 改变第74行: boolean mShowsDialog = false; 注释掉第232行:// mShowsDialo ...

  3. Android自定义View使用详细分析与绘制流程全解

    目录 目录.png 1. 自定义View基础 1.1 分类 自定义View的实现方式有以下几种 类型 定义 自定义组合控件 多个控件组合成为一个新的控件,方便多处复用 继承系统View控件 继承自Te ...

  4. Android 8.1 PowerManagerService分析(三)——WakeLock机制

    欢迎大家关注我的掘金帐号 我会在那里定期更新最新版本的Android Framework源码模块分析~~ 在Android 8.1 PowerManagerService分析(一)中,主要分析了PMS ...

  5. Android Linux Alarm 对功耗的影响

    Android Linux Alarm 对功耗的影响 技术标签: alarm  rtc  android  linux 文章目录 1. Alarm介绍 1.1 alarm的几种类型及区别 1.2 al ...

  6. android功耗测试方法,手机功耗测试指南

    和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分 功耗 知识点,通过阅读本篇文章,您将收获以下内容:1.测试功耗手机配置 2.飞行模式待机功耗 3.单SIM ...

  7. CTS(11)---android自动化测试CTS源码分析之一

    android自动化测试CTS源码分析之一 1, 概述 CTS(Compatibility Test Suite)全名兼容性测试,主要目的就是让Android设备开发商能够开发出兼容性更好的andro ...

  8. android休眠唤醒驱动流程分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11777501 标准linux休眠过程: l        power managemen ...

  9. Android车载应用开发与分析(13)- 系统设置-蓝牙设置

    1. 前言 Android 车载应用开发与分析是一个系列性的文章,这个是第13篇分析系统设置,该系列文章旨在分析原生车载Android系统中核心应用的实现方式,帮助初次从事车载应用开发的同学,更好地理 ...

最新文章

  1. IT兄弟连 Java语法教程 变量1
  2. 一步一步学lucene——(第四步:搜索篇)
  3. runtime实现的机制是什么,怎么用,一般用于干嘛. 你还能记得你所使用的相关的头文件或者某些方法的名称吗?...
  4. 基本概率模型和贝叶斯定理
  5. 中小学招生强化就近入学;首次全女性太空行走取消;苹果付费新闻APP奔溃;NASA火星上发现鹅卵石;这就是今天的大新闻...
  6. mlag 堆叠_S-MLAG解决方案介绍
  7. nuxt.js 引入第三方插件报window is not defined
  8. Java 中处理 Exception 的最佳实践
  9. 数据库基础(1)关系的布尔运算:数据集的并,交,差,补和有效补运算
  10. Linux中断处理“下半部”机制
  11. 中国有多少家银行?(最全名单统计)
  12. 力扣刷题 DAY_77 贪心
  13. 初识云原生安全:云时代的最佳保障
  14. Java SE基础知识详解第[12]期—集合(Set、Collections、Map、集合嵌套)
  15. 【C语言】函数的数组传参
  16. Linux——挂载硬盘
  17. 多功能检测按键 单按 长按 多个按键 响应方式
  18. 写一份高逼格的简历,有哪些新技能呢?
  19. Core Dump 核心转储-进程异常分析jmap命令
  20. c语言课程下载模板,万年历C语言课程设计万年历

热门文章

  1. 【AD】Altium designer pcb 交叉选择模式
  2. 百度知道回答html代码,html - JavaScript面试题:页面编码和被请求的资源编码 ,如果不一致,如何处理?百度上搜到一个答案,不知道是否正确?...
  3. 解释java程序所使用的命令是,Java程序的执行原理以及解释说明
  4. ios 按钮图片拉伸_#UIButton#背景图片的拉伸
  5. 《Reids 设计与实现》第二章 字典
  6. 【Java数据结构与算法】第十四章 红黑树
  7. Java高级开发工程师面试考纲
  8. Shiro(二)通过shiro实现登录 连接数据库+集成Springboot
  9. centos 增加分区容量
  10. Android Progurad 代码混淆