博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
安卓电量优化之WakeLock锁机制全面解析
阅读量:5283 次
发布时间:2019-06-14

本文共 4720 字,大约阅读时间需要 15 分钟。

版权声明:本文出自汪磊的博客,转载请务必注明出处。

一、WakeLock概述

wakelock是一种锁的机制,只要有应用拿着这个锁,CPU就无法进入休眠状态,一直处于工作状态。比如,手机屏幕在屏幕关闭的时候,有些应用依然可以唤醒屏幕提示用户消息,这里就是用到了wakelock锁机制,虽然手机屏幕关闭了,但是这些应用依然在运行着。手机耗电的问题,大部分是开发人员没有正确使用这个锁,成为"待机杀手"。

Android手机有两个处理器,一个叫Application Processor(AP),一个叫Baseband Processor(BP)。AP是ARM架构的处理器,用于运行Linux+Android系统;BP用于运行实时操作系统(RTOS),通讯协议栈运行于BP的RTOS之上。非通话时间,BP的能耗基本上在5mA左右,而AP只要处于非休眠状态,能耗至少在50mA以上,执行图形运算时会更高。另外LCD工作时功耗在100mA左右,WIFI也在100mA左右。一般手机待机时,AP、LCD、WIFI均进入休眠状态,这时Android中应用程序的代码也会停止执行。

Android为了确保应用程序中关键代码的正确执行,提供了Wake Lock的API,使得应用程序有权限通过代码阻止AP进入休眠状态。但如果不领会Android设计者的意图而滥用Wake Lock API,为了自身程序在后台的正常工作而长时间阻止AP进入休眠状态,就会成为待机电池杀手。

那么Wake Lock API具体有啥用呢?心跳包从请求到应答,断线重连重新登陆等关键逻辑的执行过程,就需要Wake Lock来保护。而一旦一个关键逻辑执行成功,就应该立即释放掉Wake Lock了。两次心跳请求间隔5到10分钟,基本不会怎么耗电。

二、WakeLock使用

获取WakeLock实例代码如下:

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag");

newWakeLock(int levelAndFlags, String tag)中PowerManager.PARTIIAL_WAKE_LOCK是一个标志位,标志位是用来控制获取的WakeLock对象的类型,主要控制CPU工作时屏幕是否需要亮着以及键盘灯需要亮着,标志位说明如下:

levelAndFlags CPU是否运行 屏幕是否亮着 键盘灯是否亮着
PARTIAL_WAKE_LOCK
SCREEN_DIM_WAKE_LOCK 低亮度
SCREEN_BRIGHT_WAKE_LOCK 高亮度
FULL_WAKE_LOCK

特殊说明:自API等级17开始,FULL_WAKE_LOCK将被弃用。应用应使用FLAG_KEEP_SCREEN_ON

WakeLock类可以用来控制设备的工作状态。使用该类中的acquire可以使CPU一直处于工作的状态,如果不需要使CPU处于工作状态就调用release来关闭。

(1)、自动release

如果我们调用的是acquire(long timeout)那么就无需我们自己手动调用release()来释放锁,系统会帮助我们在timeout时间后释放。

(2)、手动release

如果我们调用的是acquire()那么就需要我们自己手动调用release()来释放锁。

最后使用WakeLock类记得加上如下权限:

1 

注意:在使用该类的时候,必须保证acquirerelease是成对出现的。

三、保持屏幕常亮

最好的方式是在Activity中使用FLAG_KEEP_SCREEN_ON的Flag。

1 public class MainActivity extends Activity {2     @Override3     protected void onCreate(Bundle savedInstanceState) {4         super.onCreate(savedInstanceState);5         setContentView(R.layout.activity_main);6         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);7     }8 }

这个方法的好处是不像唤醒锁(wake locks),需要一些特定的权限(permission)。并且能正确管理不同app之间的切换,不用担心无用资源的释放问题。 

另一个方式是在布局文件中使用android:keepScreenOn属性:

1 
6 ...7

android:keepScreenOn = ”true“的作用和FLAG_KEEP_SCREEN_ON一样。使用代码的好处是你允许你在需要的地方关闭屏幕。

注意:一般不需要人为的去掉FLAG_KEEP_SCREEN_ON的flag,windowManager会管理好程序进入后台回到前台的的操作。如果确实需要手动清掉常亮的flag,使用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

四、WakefulBroadcastReceiver + IntentService实例

IntentService使用请参照我之前博客

WakefulBroadcastReceiver是BroadcastReceiver的一种特例。它会为你的APP创建和管理一个PARTIAL_WAKE_LOCK类型的WakeLock。WakefulBroadcastReceiver把工作交接给service(通常是IntentService),并保证交接过程中设备不会进入休眠状态。如果不持有WakeLock,设备很容易在任务未执行完前休眠。最终结果是你的应用不知道会在什么时候能把工作完成,相信这不是你想要的。

使用startWakefulService()方法来启动服务,与startService()相比,在启动服务的同时,并启用了唤醒锁。

当后台服务的任务完成,要调用WLWakefulReceiver.completeWakefulIntent()来释放唤醒锁。

WLWakefulReceiver类如下:

1 public class WLWakefulReceiver extends WakefulBroadcastReceiver { 2  3     private static final String TAG = "myTag"; 4  5     @Override 6     public void onReceive(Context context, Intent intent) { 7         // 8         String extra = intent.getStringExtra("msg"); 9         Log.i(TAG, "onReceive:"+extra);10         Intent serviceIntent = new Intent(context, MyIntentService.class);11         serviceIntent.putExtra("msg", extra);12        startWakefulService(context, serviceIntent);13     }14 }

很简单,就是打印一下信息以及调用startWakefulService方法来启动服务。

MyIntentService类如下:

1 public class MyIntentService extends IntentService { 2  3     private static final String TAG = "myTag"; 4      5     public MyIntentService() { 6         super("MyIntentService"); 7     } 8  9     @Override10     protected void onHandleIntent(Intent intent) {11         //子线程中执行12         Log.i(TAG, "onHandleIntent");13         for (int i = 0; i < 10; i++) {14             try {15                 Thread.sleep(3000);16                 String extra = intent.getStringExtra("msg");17                 Log.i(TAG, "onHandleIntent:"+extra);18             } catch (InterruptedException e) {19                 e.printStackTrace();20             }21         }22         //调用completeWakefulIntent来释放唤醒锁。23         WLWakefulReceiver.completeWakefulIntent(intent); 24     }25 }

同样很简单,也是打印信息进行耗时操作,但是执行完自己业务逻辑后一点记得调用completeWakefulIntent来释放唤醒锁。

最后就是启动广播接收者以及加入权限和声明了:

Intent intent = new Intent("WANG_LEI");intent.putExtra("msg", "学习WAKE_LOCK。。。");sendBroadcast(intent);

好了,编写好程序运行发现及时按电源键屏幕关闭依然有LOG打印出。

本篇到此结束,wakelock锁主要是相对系统的休眠而言的,意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了wake_lock锁。希望经过上述共同学习你能正确使用WakeLock,不要做电池杀手。

声明:文章将会陆续搬迁到个人公众号,以后文章也会第一时间发布到个人公众号,及时获取文章内容请关注公众号

 

转载于:https://www.cnblogs.com/leipDao/p/8241468.html

你可能感兴趣的文章
An easy problem
查看>>
MauiMETA工具的使用(一)
查看>>
LeetCode: Anagrams 解题报告
查看>>
用cookie登录慕课网络教学中心刷评论
查看>>
Qt 中获取本机IP地址
查看>>
基本数据类型(int, bool, str)
查看>>
070102_赌博设计:概率的基本概念,古典概型
查看>>
IT人生的价值和意义 感觉真的有了
查看>>
Linux命令之df
查看>>
JS DOM对象
查看>>
python正则表达式
查看>>
OGR – Merging Multiple SHP files
查看>>
创业公司该不该被收购?(转)
查看>>
sqlserver 行转列、列转行[转]
查看>>
【IScroll深入学习】解决IScroll疑难杂症
查看>>
python 数据类型
查看>>
108-PHP类成员protected和private成员属性不能被查看数值
查看>>
ajax post data 获取不到数据,注意contentType
查看>>
css控制height充满浏览器视口
查看>>
Linux 系统目录结构
查看>>