??從Android應用層及Framework層的角度分析WakeLock鎖機制
本篇博客撰寫思路總結和關鍵點說明:

為了更加方便的讀者閱讀博客,通過導讀思維圖的形式將本博客的關鍵點列舉出來,從而方便讀者取舍和閱讀!
前言
??好久沒有寫點偏重實戰型別的博客了,最近一直都在搗鼓原始碼分析和專案相關事情,是時候來點偏重實戰型別的博客了,捯飭點啥實戰的呢,正好前兩天有一個同事詢問我關于Android的WakeLock鎖相關的問題,雖然網上說有不少關于WakeLock鎖相關分析的博客但是都不是很完善,基本只側重了某一個點,這里我們從Android應用層及Framework層的角度出發來對Android的WakeLock鎖機制分析一番,
注意:本篇的介紹是基于Android 7.xx平臺為基礎的,其中涉及的代碼路徑如下:
frameworks/base/core/java/android/os/PowerManager.java
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
hardware/libhardware_legacy/power/power.c
在開啟本篇博客正式分析前,先奉上關于WakeLock鎖在Android原始碼中的整個層次關系,以便讀者先從整體脈絡上把握一下Android是怎么設計WakeLock鎖相關架構的,

一.WakeLock鎖機制概述和設計用途
通常我們在使用新事物之前,都有必有先對其有個大概了解,然后才能決定是否使用以及怎么使用!所以對于WakeLock鎖我們也遵循如上的邏輯進行處理,
1.1 WakeLock鎖機制概述
??在Android的世界中被叫做鎖的有很多種,譬如檔案鎖啊,執行緒鎖啊等!那么這里的WakeLock鎖又是一個啥東東呢,從字面意思理解就是休眠喚醒鎖,但是站在Android的設計者高度以及角度來看WakeLock確實也是一種鎖,它是Android框架層提供的一套機制(需要從kernel到framework層的一起配合),無論內核空間或者用戶空間只要持有了WakeLock鎖,就可以達到控制Android設備運行狀態的目的,這里的設備運行狀態主要指螢屏的常亮滅屏,鍵盤燈的常亮,CPU等保持運行的,如果一定要對WakeLock鎖有一個定義,那就是Android提供的一種機制使Android終端保持一種運行狀態,不至于CPU完全睡眠“清醒”劑!
1.2 WakeLock鎖設計用途
??那么Android為社么要設計這么一種機制呢,也許讀者會說了存在即合理了(哥不帶這么玩的嗎),我們想象一下,當我們吃著火鍋唱著歌,吃得正嗨的時候突然停電了或者沒有菜了,估計讀者此時會有罵娘的沖動了,在Android的現實世界里也會存在著這種情況,在某些場景下我們需要我們的Android終端在滅屏以后后臺任務還依然能夠運行,譬如音樂,后臺下載等,但是通常情況下手機滅屏狀態下保持一段時間后,系統會進入休眠,上述的任務就不能夠完美的執行了,而WakeLock正是為了解決這類問題應運而生的,只要我們申請了WakeLock,那么在釋放WakeLock之前,系統不會進入休眠,即使在滅屏的狀態下,應用要執行的任務依舊不會被打斷,
這里我們從實際使用角度出發簡單概括一下WakeLock鎖的各種使用場景:
- 滅屏后,要保持CPU一直運轉,然后可以正常執行后臺任務,通常是在Service中執行
- 通知、鬧鐘來臨之后,想點亮螢屏通知用戶
- 在某些情況下,應用需要保持螢屏高亮
二.WakeLock鎖分類
??WakeLock鎖機制概述和設計用途我們已經介紹清楚了,這個就好像媒婆給你介紹男女朋友,開場白已經好了,是時候開始真正的了解了不是,WakeLock鎖根據不同的使用場景可以劃分為如下幾類情況(這個沒有必要記住,只要我們能在合適的場景下使用和合適的WakeLock鎖就OK了):
-
根據WakeLock鎖有效時間劃分:WakeLock可以分為永久鎖和超時鎖,永久鎖表示只要獲取了WakeLock鎖,必須顯式的進行釋放,否則系統會一直持有該鎖,對于這種鎖一定要記得顯示釋放,否則會造成Android終端功耗過大等問題;超時鎖則是在到達給定時間后,若沒有顯示釋放鎖,則會啟動自動釋放WakeLock鎖的,其實作原理為方法內部維護了一個Handler來實作的,
-
根據釋放原則劃分:WakeLock可以分為計數鎖和非計數鎖,默認為計數鎖,如果一個WakeLock物件為計數鎖,則一次申請必須對應一次釋放;如果為非計數鎖,則不管申請多少次,一次就可以釋放該WakeLock,如果我們在創建的時候不特殊指定通常創建的是非計數鎖,
在創建了 PowerManager.WakeLock 后,有兩種機制,第一種是不計數鎖機制,另一種是計數鎖機制,可以通過 setReferenceCounted(boolean value) 來指定,一般默認為計數機制,這兩種機制的區別在于,前者無論 acquire() 了多少次,只要通過一次 release()即可解鎖,而后者正真解鎖是在( --count == 0 )的時候,同樣當 (count == 0) 的時候才會去申請加鎖,其他情況 isHeld 狀態是不會改變的,所以 PowerManager.WakeLock 的計數機制并不是正真意義上的對每次請求進行申請/釋放每一把鎖,它只是對同一把鎖被申請/釋放的次數進行了統計再正真意義上的去操作,一下進行了永久鎖的測驗: 從測驗我們可以看到使用計數和計數鎖的區別,
- 根據持鎖層級劃分:分為用戶空間層透過PowerManager拿鎖,以及kernel層直接持有Wakelock鎖(這里小伙們先有一個概念,后邊的分析就知道了)
三.WakeLock鎖類常用的方法和變數
??在正式開始介紹WakeLock鎖的使用前,我們先從原始碼角度介紹一下WakeLock鎖,它是一個PowerManager的一個內部類,其類圖如下:

其常用的的幾個方法和功能如下所示:
public void setReferenceCounted(boolean value);//設定鎖的型別是否為計數鎖,和我們前面介紹的鎖的型別對應
public void acquire();//獲取鎖
public void acquire(long timeout);//獲取鎖的型別為計時鎖
public void release();//釋放獲取的鎖
public void release(int flags);//釋放所示帶標志,只有唯一一個取值為RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY
public boolean isHeld();//判斷當前的WakeLock物件是否只持有鎖
四.WakeLock鎖的使用以及相關的引數
??好嗎,前面啰嗦了一大截!讀者也許會說這個和我們的標題是不是有沖突了,說好的實戰,實戰呢(本人承諾絕對不掛羊頭賣狗肉)!這不實戰就來了,我們這里從上層應用以及Native層使用來介紹!
4.1 Android應用層使用WakeLock鎖
Android應用層使用WakeLock鎖的程序比較簡單,可以按照如下流程進行:
- 首先在AndroidManifest.xml中申請WakeLock鎖相關的權限,如下:
<uses-permission android:name="android.permission.WAKE_LOCK" />
- 接著根據具體應用場景,申請WakeLock鎖,并使用WakeLock鎖(一定不要濫用),如下:
package com.example.test;
import android.content.Context;
import android.os.PowerManager;
public class WakeLockUtils {
private static WakeLockUtils instance = null;
private PowerManager mPowerManager = null;
private PowerManager.WakeLock mWakeLock = null;
public static WakeLockUtils getInstance(Context mContext,
final int levelAndFlags, final String tag) {
if (instance == null) {
synchronized (WakeLockUtils.class) {
if (instance == null) {
instance = new WakeLockUtils(mContext, levelAndFlags, tag);
}
}
}
return instance;
}
private WakeLockUtils(Context mContext, final int levelAndFlags,
final String tag) {
mPowerManager = (PowerManager) mContext
.getSystemService(Context.POWER_SERVICE);//獲取PowerManager建立和PowerManagerService的Binder通信通道
mWakeLock = mPowerManager.newWakeLock(levelAndFlags, tag);//獲取鎖
}
//持有鎖
public void accquire(final long timeout){
if(timeout >= 0){
mWakeLock.acquire(timeout);
}else{
mWakeLock.acquire();
}
}
//釋放鎖
public void release(){
if(mWakeLock.isHeld()){//判斷是否持有鎖
mWakeLock.release();
}
}
}
上述的流程肯定是難不倒各位讀者的了,但是這里我們需要重點關注的是newWakeLock(final int levelAndFlags, final String tag)這個方法的使用,因為傳入引數的不同那么獲取的WakeLock鎖就不同,這我們放在后面再細說!
如果覺得上面的封裝有點復雜,那么下面的代碼可能會更加得直接明了:
private void wakeLockFun(){
PowerManager mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakeLock_FUN");
mWakeLock.acquire();//獲取鎖
mWakeLock.release();//釋放鎖
}
4.2 Android的Native層使用WakeLock鎖
Android的Native層使用WakeLock鎖也不復雜,這里就不來什么具體步驟了,主要就是對wakelock的節點直接操作,寫入資料就OK了!
#define WAKE_LOCK "/sys/power/wake_lock"
#define RELEASE_WAKE_LOCK "/sys/power/wake_unlock"
static int gfd_wake_lock = -1;
static int gfd_wake_unlock = -1;
const char * wake_lock_cmd = "rpc_wake_lock_timeout";
static long long wake_lock_timeout_s = 10000000000;
//這里直接操作節點,向/sys/power/wake_lock寫入資料
static int wake_lock_fun(const char* id,int lock_type = 0, int time = 0)
{
if(gfd_wake_lock <=0)
{
gfd_wake_lock = open(WAKE_LOCK, 0x02);
}
if (gfd_wake_lock < 0)
{
fprintf(stderr, "fatal error opening \"%s\"\n", WAKE_LOCK);
LOGE(TAG,"fatal error opening \"%s\"\n", WAKE_LOCK);
return -1;
}
if(strlen(id) > 64)
{
LOGE(TAG, "rpc_wake_lock failed");
return -83;
}
int result = 0;
char wake_lock_str[256];
memset(wake_lock_str, 0, sizeof(wake_lock_str));
//默認時間是10秒鐘
if(lock_type == 0)
{
sprintf(wake_lock_str,"%s %lld", id, wake_lock_timeout_s);
}
//此處表示是我們要執行自動釋放鎖
else
{
long actual_time = 10000 + time; //計算出毫秒
char* ns_suffix = "000000"; //毫秒轉納秒的后綴
sprintf(wake_lock_str,"%s %ld%s", id, actual_time, ns_suffix);
}
result = write(gfd_wake_lock, wake_lock_str, strlen(wake_lock_str));
if(result < 0)
{
LOGE(TAG,"rpc_wake_lock The error result = %d,errno is = %d\n",errno);
return result;
}
return 0;
}
static int wake_unlock_fun(const char* id)
{
if(gfd_wake_unlock <= 0)
{
gfd_wake_unlock = open(RELEASE_WAKE_LOCK, 0x02);
}
if (gfd_wake_unlock < 0)
{
fprintf(stderr, "fatal error opening \"%s\"\n", RELEASE_WAKE_LOCK);
LOGE(TAG,"fatal error opening \"%s\"\n", RELEASE_WAKE_LOCK);
return -1;
}
if(strlen(id) > 64)
{
LOGE(TAG, "rpc_wake_unlock failed");
return -83;
}
int result = 0;
result = write(gfd_wake_unlock, id, strlen(id));
if(result < 0)
{
LOGE(TAG,"rpc_wake_unlock The error result = %d,errno is = %d\n",errno);
return result;
}
return 0;
}
int main(void){
wake_lock_fun("native_wake_lock", 1, 10000000000);
sleep(5000000000);
wake_unlock_fun("native_wake_lock");
}
4.3 Android應用層獲取WakeLock鎖時引數詳解
還記得在3.1章節的時候說newWakeLock這個方法的使用是,因為傳入引數的不同那么獲取的WakeLock鎖就不同嗎,其中具體的指代的是levelAndFlags這個引數的值,和tag沒有啥關系(這個只是相當于用戶對這個WakeLock鎖的一個別名而已)!通過獲取不同的WakeLock鎖,從而影響CPU,螢屏,以及鍵盤燈的狀態的目的!
//[PowerManager.java]
public WakeLock newWakeLock(final int levelAndFlags, final String tag) {
validateWakeLockParameters(levelAndFlags, tag);
return new WakeLock(levelAndFlags, tag,
this.mContext.getOpPackageName());
}
上述的場景也比較也比較好理解,譬如有的App界面希望一直不要滅屏的運行著,有些App可以滅屏然后只需要保持CPU運轉就可以了,那這里的levelAndFlags引數既然這么重要,看來我們有必要深挖挖了!
如果讀者只是想快速的獲取想要的鎖,那么看這里就夠了,而不需要下面的從原始碼角度理解了,但是本人還是建議讀者從原始碼角度出發理解,畢竟別人給你的永遠是別人的不是你的!
各種鎖的型別對CPU 、螢屏、鍵盤的影響:
PARTIAL_WAKE_LOCK:保持CPU 運轉,螢屏和鍵盤燈有可能是關閉的,
SCREEN_DIM_WAKE_LOCK:保持CPU 運轉,允許保持螢屏顯示但有可能是灰的,允許關閉鍵盤燈
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 運轉,允許保持螢屏高亮顯示,允許關閉鍵盤燈
FULL_WAKE_LOCK:保持CPU 運轉,保持螢屏高亮顯示,鍵盤燈也保持亮度
ACQUIRE_CAUSES_WAKEUP:強制使螢屏亮起,這種鎖主要針對一些必須通知用戶的操作.
ON_AFTER_RELEASE:當鎖被釋放時,保持螢屏亮起一段時間
從newWakeLock的第一個入參的名稱就可以看出,它分為兩部分即level(級別)和Flags(標記),我們來一一捯飭捯飭一下!
4.3.1 WakeLock鎖級別
WakeLock鎖的級別都被定義在PowerManager中,它的每個引數取值有何意思,或者蹊蹺呢,讓我們來探究一番(這里留下英文注釋,讀者可以自行理解,在最后我會加上自己的理解!最后可以比對比對,我們的理解是否一致)!
//[PowerManager.java]
/**
* Wake lock level: Ensures that the CPU is running; the screen and keyboard
* backlight will be allowed to go off.
* <p>
* If the user presses the power button, then the screen will be turned off
* but the CPU will be kept on until all partial wake locks have been released.
* </p>
*/
public static final int PARTIAL_WAKE_LOCK = 0x00000001;
/**
* Wake lock level: Ensures that the screen is on (but may be dimmed);
* the keyboard backlight will be allowed to go off.
* <p>
* If the user presses the power button, then the {@link #SCREEN_DIM_WAKE_LOCK} will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
* Contrast with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*
* @deprecated Most applications should use
* {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
* of this type of wake lock, as it will be correctly managed by the platform
* as the user moves between applications and doesn't require a special permission.
*/
@Deprecated
public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006;
/**
* Wake lock level: Ensures that the screen is on at full brightness;
* the keyboard backlight will be allowed to go off.
* <p>
* If the user presses the power button, then the {@link #SCREEN_BRIGHT_WAKE_LOCK} will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
* Contrast with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*
* @deprecated Most applications should use
* {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
* of this type of wake lock, as it will be correctly managed by the platform
* as the user moves between applications and doesn't require a special permission.
*/
@Deprecated
public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a;
/**
* Wake lock level: Ensures that the screen and keyboard backlight are on at
* full brightness.
* <p>
* If the user presses the power button, then the {@link #FULL_WAKE_LOCK} will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
* Contrast with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*
* @deprecated Most applications should use
* {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
* of this type of wake lock, as it will be correctly managed by the platform
* as the user moves between applications and doesn't require a special permission.
*/
@Deprecated
public static final int FULL_WAKE_LOCK = 0x0000001a;
/**
* Wake lock level: Turns the screen off when the proximity sensor activates.
* <p>
* If the proximity sensor detects that an object is nearby, the screen turns off
* immediately. Shortly after the object moves away, the screen turns on again.
* </p><p>
* A proximity wake lock does not prevent the device from falling asleep
* unlike {@link #FULL_WAKE_LOCK}, {@link #SCREEN_BRIGHT_WAKE_LOCK} and
* {@link #SCREEN_DIM_WAKE_LOCK}. If there is no user activity and no other
* wake locks are held, then the device will fall asleep (and lock) as usual.
* However, the device will not fall asleep while the screen has been turned off
* by the proximity sensor because it effectively counts as ongoing user activity.
* </p><p>
* Since not all devices have proximity sensors, use {@link #isWakeLockLevelSupported}
* to determine whether this wake lock level is supported.
* </p><p>
* Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}.
* </p>
*/
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020;
/**
* Wake lock level: Put the screen in a low power state and allow the CPU to suspend
* if no other wake locks are held.
* <p>
* This is used by the dream manager to implement doze mode. It currently
* has no effect unless the power manager is in the dozing state.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
* </p>
*
* {@hide}
*/
public static final int DOZE_WAKE_LOCK = 0x00000040;
/**
* Wake lock level: Keep the device awake enough to allow drawing to occur.
* <p>
* This is used by the window manager to allow applications to draw while the
* system is dozing. It currently has no effect unless the power manager is in
* the dozing state.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
* </p>
*
* {@hide}
*/
public static final int DRAW_WAKE_LOCK = 0x00000080;
/**
* Mask for the wake lock level component of a combined wake lock level and flags integer.
*
* @hide
*/
public static final int WAKE_LOCK_LEVEL_MASK = 0x0000ffff;
好了上面原版的英文注釋給出了,先給讀者5分鐘看看,我們接著引入我對上述 WakeLock鎖級別的理解,如下:
//[PowerManager.java]
/*
當我們創建的WakeLock持有該型別的鎖時候,即使我們按power按鍵使我們的Android
終端熄滅螢屏和鍵盤燈,CPU也不會進入休眠狀態從而達到保持后臺任務完美運行的目的
這種模式也是我們最經常用到的,如果對該鎖特點用一句話概述就是:
保持CPU運轉,但是鍵盤燈和螢屏可以關閉(人為,系統控制的譬如設定了多久沒有用戶操作)
注意:螢屏和鍵盤燈不受該鎖影響,可以正常熄滅不會導致該鎖釋放
*/
public static final int PARTIAL_WAKE_LOCK = 0x00000001;
/*
注意:該WakeLock鎖級別已經被標注為廢棄
當我們創建的WakeLock持有該型別的鎖時候,會保持螢屏亮著(此時螢屏也
可能會進入dimed狀態,即我們螢屏在滅屏前的一種漸暗的狀態),此時鍵盤可能會關閉
但是但是但是,當用戶按power按鍵熄滅螢屏后也會釋放該WakeLock鎖,從而CPU會進入休眠狀態
如果對該鎖特點用一句話概述就是:
保持CPU運轉,螢屏會常亮(但是可能會進入漸暗狀態),鍵盤燈可能關閉
假如Android App應用想通過此種鎖保持螢屏常亮,Android系統推薦如下方法(當Activity或view可見時,螢屏才保持常亮):
在Activity.onCreate()中: getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
或在xml布局中: android:keepScreenOn="true"
或對View設定: view.setKeepScreenOn(true);
螢屏相關的其它FLAG:
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 解鎖螢屏
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON 點亮螢屏
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 螢屏鎖定時也能顯示
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 螢屏打開時允許鎖屏
*/
@Deprecated
public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006;
/*
注意:該WakeLock鎖級別已經被標注為廢棄
并且該型別的鎖和前面的SCREEN_DIM_WAKE_LOCK非常類似,唯一的區別就是持有該鎖螢屏
會一直保持最亮的模式,不會進入漸暗的模式
并且當用戶按power按鍵熄滅螢屏后也會釋放該WakeLock鎖,從而CPU會進入休眠狀態
如果對該鎖特點用一句話概述就是:
保持CPU運轉,螢屏會常亮,鍵盤燈可能關閉
并且Android官方也是推薦替代的方案和SCREEN_DIM_WAKE_LOCK一樣,這里就復制粘貼了
*/
@Deprecated
public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a;
/*
注意:該WakeLock鎖級別已經被標注為廢棄
持有該類鎖的最大特點是鍵盤燈和螢屏保持常亮的狀態
并且當用戶按power按鍵熄滅螢屏后也會釋放該WakeLock鎖,從而CPU會進入休眠狀態
如果對該鎖特點用一句話概述就是:
保持CPU運轉,螢屏會常亮,鍵盤燈都常亮
并且Android官方也是推薦替代的方案和SCREEN_BRIGHT_WAKE_LOCK一樣,這里就復制粘貼了
*/
@Deprecated
public static final int FULL_WAKE_LOCK = 0x0000001a;
/*
該鎖比較特殊,用于和距離傳感器配合使用
持有該型別鎖的特點是:當距離傳感器檢測到有物體(包括)靠近,會將螢屏熄滅
相反,當檢測到物體遠離后會點亮螢屏
上述鎖不會影響終端的正常進入休眠狀態,只有當前螢屏由該wakelock鎖滅掉,才不會進入休眠狀態
應用場景在通話中比較常見
*/
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020;
/**************************************************************/
//上面的幾個鎖型別都是公開的,第三方App可以呼叫到的,下面的兩個比較特殊是隱藏的
/*
如果持有該鎖,則會使螢屏處于DOZE狀態,同時允許CPU掛起,該鎖用于DreamManager實作Doze模式,
如SystemUI的DozeService
Doze模式是在Android M中,Google就引入了Doze模式,它定義了一種全新的、低能耗的狀態,
在該狀態,后臺只有部分任務被允許運行,其它任務都被強制停止
*/
public static final int DOZE_WAKE_LOCK = 0x00000040;
/*
如果持有該鎖,則會使設備保持喚醒狀態,以進行繪制螢屏,該鎖常用于WindowManager中,
允許應用在系統處于Doze狀態下時進行繪制
*/
public static final int DRAW_WAKE_LOCK = 0x00000080;
上面咔咔一頓講,自我感覺有點啰嗦了,但是我覺得吧,既然是寫博客就應該整清楚,不能搞那種意猶未盡個的感覺,我們對上面的幾種WakeLock鎖整理一番,標注重點:
-
如果想保持CPU一直運轉不進入休眠(那怕是用戶按power鍵主動滅屏),請使用PARTIAL_WAKE_LOCK級別型別鎖
-
如果是想保持螢屏常量,Android建議盡量使用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)的方法,而不是使用WakeLock型別的鎖了
4.3.2 WakeLock鎖幾個常用的flag標志
通過前面我們知道WakeLock鎖不僅有各種級別,而且在同一個級別的時候采取不同的flag標志其表現形式也是不同的,讓我們來探究一番(這里留下英文注釋,讀者可以自行理解,在最后我會加上自己的理解!最后可以比對比對,我們的理解是否一致)!
//[PowerManager.java]
/**
* Mask for the wake lock level component of a combined wake lock level and flags integer.
*
* @hide
*/
public static final int WAKE_LOCK_LEVEL_MASK = 0x0000ffff;
/**
* Wake lock flag: Turn the screen on when the wake lock is acquired.
* <p>
* Normally wake locks don't actually wake the device, they just cause
* the screen to remain on once it's already on. Think of the video player
* application as the normal behavior. Notifications that pop up and want
* the device to be on are the exception; use this flag to be like them.
* </p><p>
* Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*/
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
/**
* Wake lock flag: When this wake lock is released, poke the user activity timer
* so the screen stays on for a little longer.
* <p>
* Will not turn the screen on if it is not already on.
* See {@link #ACQUIRE_CAUSES_WAKEUP} if you want that.
* </p><p>
* Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*/
public static final int ON_AFTER_RELEASE = 0x20000000;
/**
* Wake lock flag: This wake lock is not important for logging events. If a later
* wake lock is acquired that is important, it will be considered the one to log.
* @hide
*/
public static final int UNIMPORTANT_FOR_LOGGING = 0x40000000;
/**
* Flag for {@link WakeLock#release WakeLock.release(int)}: Defer releasing a
* {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
* indicates that an object is not in close proximity.
*/
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1;
好了上面原版的英文注釋給出了,先給讀者5分鐘看看,我們接著引入我對上述 WakeLock的flag標志的的理解,如下:
//[PowerManager.java]
/*
注意此變數被標注為hide,則代表只能在系統內部使用
用于根據flag判斷Wakelock的級別,如:
如內部方法中的validateWakeLockParameters判定WakeLock傳入的的是否正確
levelAndFlags & WAKE_LOCK_LEVEL_MASK
*/
public static final int WAKE_LOCK_LEVEL_MASK = 0x0000ffff;
/*
通常wakelock鎖并不會真的主動去點亮螢屏,它們只會導致螢屏打開后將保持打開狀態
如果帶有這個flag,則會在申請wakelock時就點亮螢屏,如常見通知來時螢屏亮,該flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用
這個flag標志通常用于,當我們用戶在沒有進入深休眠時,接到一個廣播或者通知,主動來電量螢屏提醒用戶某些通知
*/
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
/*
當我們盛情的鎖,在被釋放鎖時(主動或者被動),如果wakelock帶有該標志,則會小亮一會再滅屏(注意并不是說會點亮螢屏,而是說如果釋放鎖的時候螢屏是亮的),
該flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用,
*/
public static final int ON_AFTER_RELEASE = 0x20000000;
/*
和其他標記不同,該標記是作為release()方法的引數,且僅僅用于釋放PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK型別的
鎖,如果帶有該引數,則會延遲釋放鎖,直到傳感器不再感到物件接近
*/
public static final int UNIMPORTANT_FOR_LOGGING = 0x40000000;
關于WakeLock鎖的flag標志位的不是很多,這幾個flag主要起輔助的作用,WakeLock鎖的關鍵還是由它的(level)級別決定的,那么WakeLock鎖的級別和flag標志放在一起應該怎么使用呢,當然是通過"|"的操作了,實體如下:
mWakeLock = powerMg.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.ON_AFTER_RELEASE, "mWakeLock");
4.4 WakeLock各種型別鎖以及特點
經過前面的一番猛烈攻擊,我想讀者對于WakeLock的各種鎖應該有了初步的了解了,但是估計也還是有點云里霧里的,秉著擼到到底的原則,我們乘熱打鐵,將各種WakeLock各種型別鎖整理成表格的形式(主要是持有該鎖時,CPU作業狀態,螢屏表現,鍵盤燈等的表現形式),突出重點直搗黃龍!
| levelAndFlags | CPU運行狀態 | 螢屏狀態 | 鍵盤燈狀態 | 鎖的釋放 是否受Power 按鍵影響 | 備注以及需要注意地地方 |
|---|---|---|---|---|---|
| PARTIAL_WAKE_LOCK | On | On/Off | On/Off | 沒有影響 | 該鎖比較特殊,是系列鎖中釋放狀態唯一一個 不受power按鍵影響的,必須主動或者待持鎖時間到來才會釋放 |
| SCREEN_DIM_WAKE_LOCK | On | Dim(低亮度) | Off | release | API17以后已經被棄用,改用WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 用來替代該型別的鎖 |
| SCREEN_BRIGHT_WAKE_LOCK | On | Bright | Off | release | 同上 |
| SCREEN_BRIGHT_WAKE_LOCK | On | Bright | Off | release | 同上 |
| FULL_WAKE_LOCK | On | Bright | Bright | release | 同上 |
| PROXIMITY_SCREEN_OFF_WAKE_LOCK | On/Offf | Bright/Off | … | release | 不能和ACQUIRE_CAUSES_WAKEUP一起使用 |
| DOZE_WAKE_LOCK | On/Off | Off | … | release | @hide標注,允許在doze狀態下使cpu進入suspend狀態,僅在doze狀態下有效,需要android.Manifest.permission.DEVICE_POWER權限 |
| DRAW_WAKE_LOCK | On/Off | Off | … | No | @hide,允許在doze狀態下進行螢屏繪制,僅在doze狀態下有效,需要DEVICE_POWER權限 |
| ACQUIRE_CAUSES_WAKEUP | … | … | … | … | Wakelock 標記,一般情況下,獲取wakelock并不能喚醒設備,加上這個標志后,申請wakelock后也會喚醒螢屏,如通知、鬧鐘… 不能和PARTIAL_WAKE_LOCK一起使用 |
| ON_AFTER_RELEASE | … | … | … | … | Wakelock 標記,當釋放該標記的鎖時,會亮一小會再滅屏(注意必須是釋放之前螢屏的狀態是亮的,而不是主動點亮),并且不能和PARTIAL_WAKE_LOCK一起使用 |
是不是有點整懵了的感覺,其實我們只需記住一條一切只要從實際出發,抓取重點即可:
- 假如想后臺的某個任務一直運行申請PARTIAL_WAKE_LOCK的鎖即可
- 假如是想螢屏常量,使用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)即可
總之對于上述鎖的型別,我們在獲得WakeLock物件后,可以根據自己的需求來申請不同形式的鎖,從而達到我們的最終目的即可!
這里感徑訓是有必要對上述表格解釋一下:
其中CPU運行狀態:表示持有該鎖的時候CPU會不會進入休眠狀態
螢屏狀態:表示持有該型別鎖的時候,螢屏的表現形式,譬如亮,滅,或者亮的時候狀態
鍵盤燈狀態:表示持有該型別鎖的時候,鍵盤燈的表現狀態
鎖的釋放是否受Power按鍵影響:表示按power按鍵,是否會導致持有的WakeLock鎖被釋放
對于應用開發者來說,上述的鎖只能申請非@hide的鎖,即PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK、FULL_WAKE_LOCK四類,這個需要留意一下
五.WakeLock鎖呼叫流程分析
本來是將呼叫流程放在這篇博客中進行相關的分析,然后一網打盡的!但是分析分析著,一看這內容有點多啊,所以這個章節放在后面單獨成一個博客來分析!但是我們可以簡單的看下其整體框架呼叫流程圖,如下:

六.WakeLock相關問題的debug除錯方法
在本篇博客的第三節中我們有詳細介紹了WakeLock鎖的使用方法,那么在使用使用程序中,我們除開根據實際使用效果確定WakeLock鎖是否有生效外,還有沒有更加快捷的方法呢?這個肯定有,這里就給大伙安排上!
6.1 應用層使用WakeLock鎖的Debug除錯
只能說Android為了我們的開發能流暢的進行,為我們提供了強大的命令工具dumpsys,我們可以借助它實作監控應用層WakeLock鎖的功能,我們先看下沒有執行任何鎖的前提下的情況:
λ adb shell dumpsys power | grep -i wake
mWakefulness=Dozing
mWakefulnessChanging=false
mWakeLockSummary=0x40
mLastWakeTime=4068623 (137029 ms ago)
mHoldingWakeLockSuspendBlocker=false
mWakeUpWhenPluggedOrUnpluggedConfig=true
mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
mDoubleTapWakeEnabled=false
Wake Locks: size=1
DOZE_WAKE_LOCK 'DreamManagerService' ACQ=-1m16s43ms (uid=1000 pid=4346)
PowerManagerService.WakeLocks: ref count=0
可以看到上述持有一個DOZE_WAKE_LOCK 型別的鎖,這個后面會介紹到!
下面我們來申請一個鎖,執行代碼如下:
public class WakeLockTest extends Activity {
PowerManager.WakeLock mWakeLock;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.viewtest);
PowerManager mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"WakeLock_FUN");
mWakeLock.acquire();// 獲取鎖
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
if (mWakeLock.isHeld())
mWakeLock.release();// 獲取鎖
}
}
我們來看下此時的實際情況如何:
λ adb shell dumpsys power | grep -i wake
mWakefulness=Awake
mWakefulnessChanging=false
mWakeLockSummary=0x1
mLastWakeTime=4353954 (17858 ms ago)
mHoldingWakeLockSuspendBlocker=true
mWakeUpWhenPluggedOrUnpluggedConfig=true
mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
mDoubleTapWakeEnabled=false
Wake Locks: size=1
PARTIAL_WAKE_LOCK 'WakeLock_FUN' ACQ=-6s372ms (uid=1000 pid=7182)
PowerManagerService.WakeLocks: ref count=1
這里可以看到申請到了一個PARTIAL_WAKE_LOCK型別的鎖,并且其tag為WakeLock_FUN’和我們代碼申請的對上了,
6.2 Native層使用WakeLock鎖的Debug除錯
這個就沒有好說的了,簡單明了直接通過cat查看wake_lock節點即可,如下:
xxx:/ # cat /sys/power/wake_lock
PowerManagerService.Display native_wake_lock
6.3 Android系統層的WakeLock鎖的Debug除錯
系統層的WakeLock鎖Debug級別除錯起來就比較復雜了,這個涉及的知識層面比較多,并且需要對PowerManagerService有比較深入的了解和掌握了,這個屬于高階的范疇了,但是我們的dumpsy power依然能排上用場,并且最好將PowerManagerService中的除錯DEBUG打開!
關于這個我就不過多的介紹了,感興趣的讀者可以詳讀如何分析WakeLock持鎖問題!
寫在最后
??到這里,本篇從Android應用層及Framework層的角度分析WakeLock鎖機制就到這里了,通過這篇博客我想讀者應該對WakeLock有了一個比較深入的了解了,無論是從它的設計角度出發,使用場景,具體的使用應該都是得心應手的了,限于篇幅這里還有所欠缺的是WakeLock鎖的呼叫流程的詳細分析,這個我們將會在后面的博客中補上,如果本篇博客對你有所幫助,歡迎點贊和評論,當然也可以拍磚,總之歡迎留下你的腳步!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/225882.html
標籤:其他
上一篇:數星星(前綴和)
