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里的代码然后进行说明。
如果仅仅是要控制屏幕的点亮,可以使用android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON代替,这个可以更好地被平台所控制 ,当用户在应用间切换并且不需要特定的权限(link android.Manifest.permission#WAKE_LOCK)。
|
|