PowerManagerService第二讲之WakeLock

WakeLock是一种锁机制,只要有进程持有这个锁,CPU就一直在工作,系统就不会进入到休眠状态。

关于WakeLock的使用方法其实在PowerManager类中的注释已经给出了,来看描述:

    /**
     * This class gives you control of the power state of the device.
     *
     * <p>
     * <b>Device battery life will be significantly affected by the use of this API.</b>
     * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels
     * possible, and be sure to release them as soon as possible.
     * </p><p>
     * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}.
     * This will create a {@link PowerManager.WakeLock} object.  You can then use methods
     * on the wake lock object to control the power state of the device.
     * </p><p>
     * In practice it's quite simple:
     * {@samplecode
     * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
     * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
     * wl.acquire();
     *   ..screen will stay on during this section..
     * wl.release();
     * }
     * </p><p>
     * The following wake lock levels are defined, with varying effects on system power.
     * <i>These levels are mutually exclusive - you may only specify one of them.</i>

即代码实现是:

     PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
     PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
     wl.acquire();
     //screen will stay on during this section..
     wl.release();

我们来看newWakeLock方法:

    public WakeLock newWakeLock(int levelAndFlags, String tag) {
        validateWakeLockParameters(levelAndFlags, tag);
        // 创建一个WakeLock并返回
        return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
    }

    /** @hide */
    public static void validateWakeLockParameters(int levelAndFlags, String tag) {
        switch (levelAndFlags & WAKE_LOCK_LEVEL_MASK) {
            case PARTIAL_WAKE_LOCK:
            case SCREEN_DIM_WAKE_LOCK:
            case SCREEN_BRIGHT_WAKE_LOCK:
            case FULL_WAKE_LOCK:
            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
            case DOZE_WAKE_LOCK:
            case DRAW_WAKE_LOCK:
                break;
            default:
                throw new IllegalArgumentException("Must specify a valid wake lock level.");
        }
        // tag不为空,否则会抛异常
        if (tag == null) {
            throw new IllegalArgumentException("The tag must not be null.");
        }
    }

来看WakeLock的创建:

        WakeLock(int flags, String tag, String packageName) {
            // flag参数
            mFlags = flags;
            // tag不为空
            mTag = tag;
            mPackageName = packageName;
            // 创建一个Binder对象
            mToken = new Binder();
            mTraceName = "WakeLock (" + mTag + ")";
        }

1.wakelock level(唤醒锁等级)

PARTIAL_WAKE_LOCK:确保CPU正在运行,屏幕和键盘背光灯允许熄灭;

SCREEN_DIM_WAKE_LOCK(废弃):确保屏幕打开(但可能变暗),键盘背光灯允许熄灭;如果按下Power键,还是会进入休眠状态;

SCREEN_BRIGHT_WAKE_LOCK(废弃):确保屏幕以全亮度开启,键盘背光灯允许熄灭;如果按下Power键,还是会进入休眠状态;

FULL_WAKE_LOCK(废弃):确保屏幕和键盘背光灯在全亮度;如果按下Power键,还是会进入休眠状态;

PROXIMITY_SCREEN_OFF_WAKE_LOCK:当传感器激活时,关闭屏幕;物体移开后不久,屏幕再次打开;

DOZE_WAKE_LOCK(@hide):将屏幕置于低功耗状态,并允许CPU挂起;

DRAW_WAKE_LOCK(@hide):使设备保持足够的唤醒状态以允许进行绘图;

2.wakelock flag(唤醒锁标志)

ACQUIRE_CAUSES_WAKEUP: 在获取之后锁定屏幕;

ON_AFTER_RELEASE:当锁释放之后,系统会延迟一会再黑屏;

UNIMPORTANT_FOR_LOGGING:这个锁对于记录事件并不重要,如果以后获取了重要的唤醒锁,它将被视为要记录的唤醒锁;

3.acquire(申请锁)

当创建wakelock完成后,需要申请wakelock来确保屏幕正常工作。来看源码实现:

        public void acquire() {
            // 加上同步锁,mToken
            synchronized (mToken) {
                // 调用acquireLocked函数
                acquireLocked();
            }
        }

        // 含参的acquire方法
        public void acquire(long timeout) {
            synchronized (mToken) {
                acquireLocked();
                mHandler.postDelayed(mReleaser, timeout);
            }
        }

可以看到无论是无参还是含参的acquire方法都会去调用acquireLocked方法,来看:

        private void acquireLocked() {
            mInternalCount++;
            mExternalCount++;
            // 唤醒锁非计数引入或计数为1
            if (!mRefCounted || mInternalCount == 1) {
                // Do this even if the wake lock is already thought to be held (mHeld == true)
                // because non-reference counted wake locks are not always properly released.
                // For example, the keyguard's wake lock might be forcibly released by the
                // power manager without the keyguard knowing.  A subsequent call to acquire
                // should immediately acquire the wake lock once again despite never having
                // been explicitly released by the keyguard.
                mHandler.removeCallbacks(mReleaser);
                Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
                try {
                    // 调用PowerManagerService中的acquireWakeLock
                    mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                            mHistoryTag);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                mHeld = true;
            }
        }

接着看PowerManagerService的acquireWakeLock方法的实现:

        @Override // Binder call
        public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
                WorkSource ws, String historyTag) {
            if (lock == null) {
                throw new IllegalArgumentException("lock must not be null");
            }
            if (packageName == null) {
                throw new IllegalArgumentException("packageName must not be null");
            }
            PowerManager.validateWakeLockParameters(flags, tag);

            // 检查 WAKE_LOCK 权限
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
            if ((flags & PowerManager.DOZE_WAKE_LOCK) != 0) {
                mContext.enforceCallingOrSelfPermission(
                        android.Manifest.permission.DEVICE_POWER, null);
            }
            if (ws != null && !ws.isEmpty()) {
                // 如果ws不为空,需要检查UPDATE_DEVICE_STATS 权限
                mContext.enforceCallingOrSelfPermission(
                        android.Manifest.permission.UPDATE_DEVICE_STATS, null);
            } else {
                ws = null;
            }

            final int uid = Binder.getCallingUid();
            final int pid = Binder.getCallingPid();
            final long ident = Binder.clearCallingIdentity();
            try {
                // 调用acquireWakeLockInternal函数
                acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

4.release(释放锁)

acquire申请锁了之后,进程一直持锁处于亮屏等状态,当结束这些状态后,我们需要将锁释放而不能一直持有。这时会去调用release方法,来看源码实现:

    public void release() {
        release(0);
    }

    public void release(int flags) {
        // 同步锁,mToken
        synchronized (mToken) {
            if (mInternalCount > 0) {
                // internal count must only be decreased if it is > 0 or state of
                // the WakeLock object is broken.
                mInternalCount--;
            }
            if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
                mExternalCount--;
            }
            // 唤醒锁非引用计数或内部计数为0
            if (!mRefCounted || mInternalCount == 0) {
                mHandler.removeCallbacks(mReleaser);
                if (mHeld) {
                    Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                    try {
                        // 调用PowerManagerService的releaseWakeLock函数
                        mService.releaseWakeLock(mToken, flags);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                    mHeld = false;
                }
            }
            // 唤醒锁引用计数且计数<0,抛WakeLock under-locked异常
            if (mRefCounted && mExternalCount < 0) {
                throw new RuntimeException("WakeLock under-locked " + mTag);
            }
        }
    }

5.计数/不计数锁模式

在上面讲acquire和release方法的时候有涉及到计数这个词。默认情况下,唤醒锁是引用计数,如果唤醒锁是引用计数,则每次对acquire的调用都必须通过对release的相等数量的调用来平衡。即申请一次wakeLock,需要释放对应的一次;

而如果唤醒锁是不计数的,那么一次release的调用就可以解掉wakeLock锁,而不论acquire调用了几次。

源码中是通过mRefCounted这个布尔值来控制的:true表示唤醒锁引用计数;false表示不包含唤醒锁引用。可以通过setReferenceCounted方法来对mRefCounted来赋值。

        public void setReferenceCounted(boolean value) {
            synchronized (mToken) {
                mRefCounted = value;
            }
        }

        // 默认为true,即引用计数
        private boolean mRefCounted = true;

PS:源码是基于Android 10平台

发布了18 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zplxl99/article/details/103021323
今日推荐