PowerManager.WakeLock电源管理

PowerManager.WakeLock 有加锁和解锁两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非显式的放开,是不会解锁的,所以这种锁用起来要非常的小心。第二种锁是超时锁,这种锁会在锁住后一段时间解锁。

在创建了 PowerManager.WakeLock 后,有两种机制,第一种是不计数锁机制,另一种是计数锁机制。可以通过 setReferenceCounted( boolean value) 来指定,一般默认为计数机制。这两种机制的区别在于,前者无论 acquire() 了多少次,只要通过一次 release() 即可解锁。而后者正真解锁是在( --count == 0 )的时候,同样当 (count == 0) 的时候才会去申请加锁,其他情况 isHeld 状态是不会改变的。所以 PowerManager.WakeLock 的计数机制并不是正真意义上的对每次请求进行申请/释放每一把锁,它只是对同一把锁被申请/释放的次数进行了统计再正真意义上的去操作。一下进行了永久锁的测试: 从测试我们可以看到使用计数和计数锁的区别。

 
 

 

 

接下来,我们来分析一下 PowerManager.WakeLock 超时锁的机制。我们可以通过 acquire( long timeout) 来使用,在源码中我们可以看到

public void acquire( long timeout) {

acquire();

mHandler .postDelayed( mReleaser , timeout);

}

在申请一把锁的同时发送了 release() 锁的延时消息。在时间到达后自動释放。在来看看源码中 acquire release 代码:

public void acquire(){

synchronized ( mToken ) {

if (! mRefCounted || mCount ++ == 0) {

try {

mService .acquireWakeLock( mFlags , mToken , mTag );

} catch (RemoteException e) {

}

mHeld = true ;

}

}

}

 

public void release( int flags){

synchronized ( mToken ) {

if (! mRefCounted || -- mCount == 0) {

try {

mService .releaseWakeLock( mToken , flags);

} catch (RemoteException e) {

}

Held = false ;

}

if ( mCount < 0) {

throw new RuntimeException("WakeLock under-locked "+mTag);

}

}

}

在源码中我们可以看出计数机制在达到一定条件下才会去改变锁的状态,而不计数机制就每次请求改变一次。在 release( int flags) 中我们还可以看到那么一段代码

if ( mCount < 0) {

throw new RuntimeException("WakeLock under-locked "+mTag);

}

这段代码起什么效果呢?为什么要判断并抛出异常呢?其实对于非计数机制来讲,这代码的用处相当于 0 ,因为 mRefCounte 状态是 false ,永远不会去判断 mCount 标志去进行操作的。但是对于计数锁机制来讲,这句话至关重要,一旦系统中的 mCount < 0 时,再去申请锁的时候将达不到申请锁的条件 mCount ++ == 0 这将导致系统申请不到需要的锁,所以 google 工程师在这里手动抛出了异常,让系统重置。

但是为什么会出现 mCount < 0 的情况呢?原因在于使用计数锁的时候 release 次数大于 acquire 次数的时候,将会导致系统再次申请锁失效。这种情况是工测师逻辑不严谨导致的,但是还有一种特殊情况就是使用计数锁和超时锁的时候,这种情况是由于时间戳导致的,我们来看看一段代码(计数锁情况):

private void wakeUpScreen() {

synchronized ( this ) {

if ( mWakeLock .isHeld()) {

mWakeLock .release();

}

mWakeLock .acquire(3000);

}

}

假设系统在 3s 里调用了两次以上方法,第一次成功调用后,系统锁计数为 1 ,在第一次释放没有到时的情况下,我们再此调用此方法,这时判断到锁的状态为 held ,这时会去释放一次, count 变为 0 。在第二次 acquire 前,系统解锁到第一次 release 锁的消息,这时会再释放一次,但是这时候, count 已经是 0 了,导致系统 count 变为负数,抛出异常,系统重启重置,保证锁的安全性。

 

 

 

 

我们可能会问为什么不在手动 release 的时候把存在的 Delayed 消息 remove 掉,这样做的坏处是不能保证 Delayed 消息是同一个地方发出的,将会把其他地方调用的消息给处理掉,这并不符合逻辑要求。

 

但是又要使用超时锁和计数机制的发开者该怎样避免这种情况呢?一是保证你在 Delayed 消息时间呢不会被调用多次;二是不依赖超时锁的机制,在自己代码中定义延时消息,这样可以保证在同一个地方发送消息和销毁消息,保证不会去释放空锁;

 

PS :为了避免释放空锁的情况,导致下次申请不到锁的情况,为什么要抛出异常,中断系统呢?为什么不在 release 的时候加以判断,判断现在 count 是否为 0 ,而直接返回不操作呢?为什么不在发现 count 为负数的时候就把 count 重置为 0 ,保证下次申请锁成功呢?(不知道 google 工程师有什么后续的考虑) , 这个疑问留给读者一起进行思考。

 

 

学习android一段时间了,为了进一步了解android的应用是如何设计开发的,决定详细研究几个开源的android应用。从一些开源应用中吸收点东西,一边进行量的积累,一边探索android的学习研究方向。这里我首先选择了jwood的 Standup Timer  项目。本文将把研究的内容笔记整理,建立一个索引列表。

PowerManager.WakeLock

  PowerManager.WakerLock是我分析Standup Timer源代码时发现的一个小知识点,Standup Timer 用WakeLock保证程序运行时保持手机屏幕的恒亮(程序虽小但也做得相当的细心,考虑的很周到)。PowerManager 和PowerManager.WakerLock用于对Android设备的电源进行管理。
   PowerManager :This class gives you control of the power state of the device.
   PowerManager.WakeLock : lets you say that you need to have the device on.
  Android中通过各种Lock锁对电源进行控制,需要注意的是加锁和解锁必须成对出现。先上一段Standup Timer里的代码然后进行说明。

    
    
  1. private   void  acquireWakeLock() {  
  2.      if  (wakeLock ==  null ) {  
  3.             Logger.d("Acquiring wake lock" );  
  4.             PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  
  5.             wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, this .getClass().getCanonicalName());  
  6.             wakeLock.acquire();  
  7.         }  
  8.       
  9. }  
  10.   
  11.   
  12. private   void  releaseWakeLock() {  
  13.     if  (wakeLock !=  null  && wakeLock.isHeld()) {  
  14.         wakeLock.release();  
  15.         wakeLock = null ;  
  16.     }  
  17.   
  18. }  
Java代码 复制代码
  1. private void acquireWakeLock() {   
  2.      if (wakeLock == null) {   
  3.             Logger.d("Acquiring wake lock");   
  4.             PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);   
  5.             wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass().getCanonicalName());   
  6.             wakeLock.acquire();   
  7.         }   
  8.        
  9. }   
  10.   
  11.   
  12. private void releaseWakeLock() {   
  13.     if (wakeLock != null && wakeLock.isHeld()) {   
  14.         wakeLock.release();   
  15.         wakeLock = null;   
  16.     }   
  17.   
  18. }  
  1. private void acquireWakeLock() {  
  2.      if (wakeLock == null) {  
  3.             Logger.d("Acquiring wake lock");  
  4.             PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  
  5.             wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass().getCanonicalName());  
  6.             wakeLock.acquire();  
  7.         }  
  8.       
  9. }  
  10.   
  11.   
  12. private void releaseWakeLock() {  
  13.     if (wakeLock != null && wakeLock.isHeld()) {  
  14.         wakeLock.release();  
  15.         wakeLock = null;  
  16.     }  
  17.   
  18. }  

 

acquireWakeLock()方法中获取了 SCREEN_DIM_WAKE_LOCK锁,该锁使 CPU 保持运转,屏幕保持亮度(可以变灰)。这个函数在Activity的 onResume中被调用。releaseWakeLock()方法则是释放该锁。它在Activity的 onPause中被调用。利用Activiy的生命周期,巧妙的让 acquire()和release()成对出现。

  1. @Override   
  2. protected   void  onResume()  
  3. {  
  4.     super .onResume();  
  5.             //获取锁,保持屏幕亮度   
  6.     acquireWakeLock();  
  7.     startTimer();  
  8. }  
  9.   
  10. @Override   
  11. protected   void  onPause()  
  12. {  
  13.     super .onPause();  
  14.      synchronized ( this ) {  
  15.             cancelTimer();  
  16.             releaseWakeLock();  
  17.   
  18.             if  (finished) {  
  19.                 clearState();  
  20.             } else  {  
  21.                 saveState();  
  22.             }  
  23.         }  
  24. }  
Java代码 复制代码
  1. @Override  
  2. protected void onResume()   
  3. {   
  4.     super.onResume();   
  5.             //获取锁,保持屏幕亮度   
  6.     acquireWakeLock();   
  7.     startTimer();   
  8. }   
  9.   
  10. @Override  
  11. protected void onPause()   
  12. {   
  13.     super.onPause();   
  14.      synchronized(this) {   
  15.             cancelTimer();   
  16.             releaseWakeLock();   
  17.   
  18.             if (finished) {   
  19.                 clearState();   
  20.             } else {   
  21.                 saveState();   
  22.             }   
  23.         }   
  24. }  
  1. @Override  
  2. protected void onResume()  
  3. {  
  4.     super.onResume();  
  5.             //获取锁,保持屏幕亮度   
  6.     acquireWakeLock();  
  7.     startTimer();  
  8. }  
  9.   
  10. @Override  
  11. protected void onPause()  
  12. {  
  13.     super.onPause();  
  14.      synchronized(this) {  
  15.             cancelTimer();  
  16.             releaseWakeLock();  
  17.   
  18.             if (finished) {  
  19.                 clearState();  
  20.             } else {  
  21.                 saveState();  
  22.             }  
  23.         }  
  24. }  
PowerManager和WakeLock的操作步骤

  1. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);通过 Context.getSystemService() .方法获取PowerManager实例。
  2. 然后通过PowerManager的newWakeLock ((int flags, String  tag)来生成WakeLock实例。int Flags指示要获取哪种WakeLock,不同的Lock对cpu 、屏幕、键盘灯有不同影响。
  3. 获取WakeLock实例后通过acquire()获取相应的锁,然后进行其他业务逻辑的操作,最后使用release()释放(释放是必须的)。

关于int flags

各种锁的类型对CPU 、屏幕、键盘的影响:

    PARTIAL_WAKE_LOCK :保持CPU 运转,屏幕和键盘灯有可能是关闭的。

    SCREEN_DIM_WAKE_LOCK :保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯

    SCREEN_BRIGHT_WAKE_LOCK :保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯

    FULL_WAKE_LOCK :保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度

    ACQUIRE_CAUSES_WAKEUP :Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.

    ON_AFTER_RELEASE :f this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer. This can be used to reduce flicker if you are cycling between wake lock conditions.

权限获取

要进行电源的操作需要在AndroidManifest.xml中声明该应用有设置电源管理的权限。
    
    
< uses-permission android:name ="android.permission.WAKE_LOCK" /> 你可能还需要 < uses-permission android:name ="android.permission.DEVICE_POWER" />
另外WakeLock的设置是 Activiy 级别的,不是针对整个Application应用的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PowerManager.WakeLock 是一个 Android 中的类,用于管理设备电源的锁定状态。它允许应用程序在一些特殊的情况下保持设备处于唤醒状态,以确保应用程序可以继续运行。 在 Android 中,设备通常会在一段时间后进入休眠状态,以节省电量。在这种情况下,CPU 和其他一些硬件组件会进入低功耗模式,此时应用程序的运行也会受到限制。 PowerManager.WakeLock 的作用就是可以防止设备进入休眠状态,以保持设备的唤醒状态。这样,应用程序可以继续执行一些需要持续运行的操作,如后台下载、音乐播放、定时任务等。 使用 PowerManager.WakeLock 需要以下几个步骤: 1. 获取 PowerManager 的实例: ```java PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); ``` 2. 创建 WakeLock 对象: ```java PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTag"); ``` 这里的第一个参数表示锁定级别,常见的有: - PARTIAL_WAKE_LOCK:部分唤醒锁定,保持 CPU 运行,但允许屏幕关闭。 - SCREEN_DIM_WAKE_LOCK:屏幕亮度唤醒锁定,保持 CPU 运行和屏幕亮度降低。 - SCREEN_BRIGHT_WAKE_LOCK:屏幕高亮度唤醒锁定,保持 CPU 运行和屏幕高亮度。 - FULL_WAKE_LOCK:全唤醒锁定,保持 CPU 运行和屏幕高亮度。 第二个参数是一个标签,用于标识唤醒锁定的用途。 3. 获取唤醒锁: ```java wakeLock.acquire(); ``` 这个方法会请求获取唤醒锁定,使设备保持唤醒状态。 4. 释放唤醒锁: ```java wakeLock.release(); ``` 当不再需要保持设备唤醒状态时,需要调用 release 方法释放唤醒锁定。 需要注意的是,使用 PowerManager.WakeLock 要谨慎,确保在不需要时及时释放锁定,以免造成电池消耗过大。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值