Android WakeLock详解

目录


前言

不知道大家是否也想过,当你手机灭屏的时候,为什么一条微信或者QQ信息能够点亮你的屏幕?
答案就是Android的WakeLock机制。这篇文章主要是介绍如何使用WakeLock,应该还达不到详解的地步,各位同学感兴趣的可以看一下WakeLock的基本使用方法。


WakeLock使用

我们先来看一下Android官方对PowerManager和WakeLock的注解:

  • PowerManager:This class gives you control of the power state of the device.
  • WakeLock:A wake lock is a mechanism to indicate that your application needs to have the device stay on.

WakeLock levelAndFlags和使用场景

Level保持CPU保持屏幕亮保持键盘亮使用场景
PARTIAL_WAKE_LOCK长时间运行的后台服务,例如Service等
SCREEN_DIM_WAKE_LOCK低亮度除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式
SCREEN_BRIGHT_WAKE_LOCK高亮度除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式
FULL_WAKE_LOCK高亮度除非必须保持CPU运行直至运算完成,否则请使用FLAG_KEEP_SCREEN_ON方式

除了这四个Level之外,PowerMager还提供了两个Flag,可以配合Level使用。

FLAG描述
ACQUIRE_CAUSES_WAKEUP默认情况下wake locks并不是马上开启CPU、Screen或者Keyboard的illumination(对于Screen是Dim或Bright,Keyboard是Bright. wake locks只是在被开启后(比如用户的活动),让设备延续(保存)你设定开启的状态. 但是如果加上ACQUIRE_CAUSES_WAKEUP就可以让Screen或Keyboar的illumination没开启的情况,马上开启它们。 典型的应用就是在收到一个重要的notifications时,需要马上点亮屏幕。
ON_AFTER_RELEASE当wake lock被释放的时候,当前调用wake lock的activity的计数器会被重置,所以屏幕会继续亮一段时间

注意:
这两个Flag和PARTIAL_WAKE_LOCK组合是没有作用的。


参考场景

写一个应用,可以完成如下场景:

  1. 主Activity点击一个Button,起了一个线程,先sleep 10秒,然后关闭屏幕。
  2. 线程里去获取WakeLock锁,点亮屏幕并打印信息到日志中。

具体代码如下:

MainActivity.java

    private void testWakeLock() {
        new Thread(new Runnable() {
            private void printLog() {
                for (int i = 0; i < 10; i ++) {
                    Log.e("wangzhengyi", "hello log " + i);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void run() {
                Log.e("wangzhengyi", "ready to acquire wakelock!");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                TestWakeLock twl = new TestWakeLock(MainActivity.this);
                twl.acquireWakeLock();
                printLog();
                twl.releaseWakeLock();
            }
        }).start();
    }

TestWakeLock.java

import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;

public class TestWakeLock {
    private WakeLock mWakeLock;
    private Context mContext;

    public TestWakeLock(Context context) {
        this.mContext = context;
    }

    public void acquireWakeLock() {
        if (mWakeLock == null) {
            PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "ZHENGYI.WZY");
            if (mWakeLock != null) {
                mWakeLock.acquire();
                Log.e("wangzhengyi", "get powermanager wakelock!");
            }
        }
    }

    public void releaseWakeLock() {
        if (mWakeLock != null) {
            mWakeLock.release();
            Log.e("wangzhengyi", "release powermanager wakelock!");
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK"/>

WakeLock源码分析

不深入到WakeLock源码,怎么敢称为详解,对吧!!接下来,我们看一下WakeLock的源码实现。

    public WakeLock newWakeLock(int levelAndFlags, String tag) {
        validateWakeLockParameters(levelAndFlags, tag);
        return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
    }

    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:
                break;
            default:
                throw new IllegalArgumentException("Must specify a valid wake lock level.");
        }
        if (tag == null) {
            throw new IllegalArgumentException("The tag must not be null.");
        }
    }

可以看到,newWakeLock方法首先检测LevelAndFlags和Tag的合法性,代码很简单,大家可以自己看一下。其实就是:tag不能为空,Level必须用PowerManager提供的几个Level。

接下来,我们就进入到WakeLock的构造函数了。WakeLock是PowerManager的内部类。这里我删除了WakeLock类中暂时我们用不到的方法。

    public final class WakeLock {
        private final int mFlags;
        private final String mTag;
        private final String mPackageName;
        private final IBinder mToken;
        private int mCount;
        private boolean mRefCounted = true;
        private boolean mHeld;

        private final Runnable mReleaser = new Runnable() {
            public void run() {
                release();
            }
        };

        WakeLock(int flags, String tag, String packageName) {
            mFlags = flags;
            mTag = tag;
            mPackageName = packageName;
            mToken = new Binder();
        }

        /**
         * Acquires the wake lock.
         * <p>
         * Ensures that the device is on at the level requested when the wake
         * lock was created.
         * </p>
         */
        public void acquire() {
            synchronized (mToken) {
                acquireLocked();
            }
        }

        private void acquireLocked() {
            if (!mRefCounted || mCount++ == 0) {
                // 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);
                try {
                    mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource);
                } catch (RemoteException e) {
                }
                mHeld = true;
            }
        }

        /**
         * Releases the wake lock.
         * <p>
         * This method releases your claim to the CPU or screen being on. The
         * screen may turn off shortly after you release the wake lock, or it
         * may not if there are other wake locks still held.
         * </p>
         */
        public void release() {
            release(0);
        }

        /**
         * Releases the wake lock with flags to modify the release behavior.
         * <p>
         * This method releases your claim to the CPU or screen being on. The
         * screen may turn off shortly after you release the wake lock, or it
         * may not if there are other wake locks still held.
         * </p>
         *
         * @param flags
         *            Combination of flag values to modify the release behavior.
         *            Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is
         *            supported.
         *
         *            {@hide}
         */
        public void release(int flags) {
            synchronized (mToken) {
                if (!mRefCounted || --mCount == 0) {
                    mHandler.removeCallbacks(mReleaser);
                    if (mHeld) {
                        try {
                            mService.releaseWakeLock(mToken, flags);
                        } catch (RemoteException e) {
                        }
                        mHeld = false;
                    }
                }
                if (mCount < 0) {
                    throw new RuntimeException("WakeLock under-locked " + mTag);
                }
            }
        }

        /**
         * Returns true if the wake lock has been acquired but not yet released.
         *
         * @return True if the wake lock is held.
         */
        public boolean isHeld() {
            synchronized (mToken) {
                return mHeld;
            }
        }
    }

我们以acquire方法为例,通过对源码的分析,我们发现获取WakeLock的实现是通过mService进行的:

mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource);

而mService是在PowerManager类里实例化的:

final IPowerManager mService;

mService实例化类为/frameworks/base/services/java/com/android/server/power/PowerManagerService.java,而到这里类里,你最终发现acquireWakeLock是由JNI层的native方法实现的。

private static native void nativeAcquireSuspendBlocker(String name);

而这个方法的实现是在/frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp代码中:

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

这个acquire_wake_lock是在/hardware/libhardware_legacy/power/power.c里定义的

acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();

//    ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));
}

可以看到,acquire wake lock真正的实现是在fd所指向的文件中写了一串字符即可。
fd所指向的文件定义如下:

const char * const NEW_PATHS[] = {
    "/sys/power/wake_lock",
    "/sys/power/wake_unlock",
};

结束语

好了,到此Android WakeLock分析结束,欢迎大家拍砖

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值