Android(安卓)WakeLock详解
目录
- 目录
- 前言
- 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组合是没有作用的。
参考场景
写一个应用,可以完成如下场景:
- 主Activity点击一个Button,起了一个线程,先sleep 10秒,然后关闭屏幕。
- 线程里去获取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分析结束,欢迎大家拍砖
更多相关文章
- Android(安卓)进阶学习:事件分发机制完全解析,带你从源码的角度彻
- Android——横屏和竖屏的切换,以及明文密码的显示
- Android中称为四大组件
- Andriod学习笔记(二)
- Android(安卓)从源码分析View层次之ActionMode
- Android高手进阶教程(十)之----Android(安卓)PopupWindow的使用!
- MVC、MVP的讲解,如优缺点,使用需求场景
- DDPush开源推送框架源码分析之APPServer到DDPush
- Android(安卓)ZXing源码简化