不知道大家是否也想过,当你手机灭屏的时候,为什么一条微信或者QQ信息能够点亮你的屏幕?
答案就是Android的WakeLock机制。这篇文章主要是介绍如何使用WakeLock,应该还达不到详解的地步,各位同学感兴趣的可以看一下WakeLock的基本使用方法。
我们先来看一下Android官方对PowerManager和WakeLock的注解:
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组合是没有作用的。
写一个应用,可以完成如下场景:
具体代码如下:
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的源码实现。
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分析结束,欢迎大家拍砖
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/wzy_1988/article/details/46875343