目录

  • 目录
  • 前言
  • WakeLock使用
    • WakeLock levelAndFlags和使用场景
    • 参考场景
  • 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分析结束,欢迎大家拍砖

更多相关文章

  1. Android(安卓)进阶学习:事件分发机制完全解析,带你从源码的角度彻
  2. Android——横屏和竖屏的切换,以及明文密码的显示
  3. Android中称为四大组件
  4. Andriod学习笔记(二)
  5. Android(安卓)从源码分析View层次之ActionMode
  6. Android高手进阶教程(十)之----Android(安卓)PopupWindow的使用!
  7. MVC、MVP的讲解,如优缺点,使用需求场景
  8. DDPush开源推送框架源码分析之APPServer到DDPush
  9. Android(安卓)ZXing源码简化

随机推荐

  1. React Native For Android(安卓)Windows
  2. ANDROID2.3改进
  3. Android(安卓)遍历Hashmap里面的key 和va
  4. Android检查网络是否可用及上网请求
  5. Android相机启动crash错误排查
  6. 【Android(安卓)MyEclipse】no projects
  7. Android(安卓)ActionBar使用
  8. android review--基础知识
  9. 如何让自己写的apk获得系统权限
  10. 常见android中的style