PowerManager.WakeLock源码解读(By DADA)

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

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


 

 

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

publicvoidacquire(longtimeout) {

acquire();

mHandler.postDelayed(mReleaser,timeout);

}

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

publicvoidacquire(){

synchronized(mToken) {

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

try{

mService.acquireWakeLock(mFlags,mToken,mTag);

} catch(RemoteException e) {

}

mHeld= true;

}

}

}

 

publicvoidrelease(intflags){

synchronized(mToken) {

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

try{

mService.releaseWakeLock(mToken,flags);

} catch(RemoteException e) {

}

Held= false;

}

if(mCount <0) {

thrownew RuntimeException("WakeLock under-locked "+mTag);

}

}

}

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

if(mCount <0) {

thrownew RuntimeException("WakeLock under-locked "+mTag);

}

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

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

privatevoidwakeUpScreen() {

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里的代码然后进行说明。

      
      
      
      
view plain print ?
  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. }  

 

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

view plain print ?
  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. }  
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应用的。
本文章转载自 http://www.cnblogs.com/keyindex/articles/1819504.html

你可能感兴趣的:(PowerManager.WakeLock源码解读(By DADA))