Android WakeLock詳解,androidwakelock
目錄
- 目錄
- 前言
- 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組合是沒有作用的。
參考情境
寫一個應用,可以完成如下情境:
具體代碼如下:
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分析結束,歡迎大家拍磚
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。