Android 功耗分析之wakelock

生活總是讓我們遍體鱗傷烹困,但到后來(lái)晶渠,那些受傷的地方一定會(huì)變成我們最強(qiáng)壯的地方。---海明威

WakeLock是什么

WakeLock是Android框架層提供的一套機(jī)制曲伊,應(yīng)用使用該機(jī)制可以達(dá)到控制Android設(shè)備狀態(tài)的目的叽讳。這里的設(shè)備狀態(tài)主要指屏幕的打開(kāi)關(guān)閉,cpu的保持運(yùn)行坟募。簡(jiǎn)單的理解WakeLock是讓系統(tǒng)保持"清醒"的一種手段.

WakeLock作用

當(dāng)手機(jī)滅屏狀態(tài)下保持一段時(shí)間后岛蚤,系統(tǒng)會(huì)進(jìn)入休眠,一些后臺(tái)運(yùn)行的任務(wù)就可能得不到正常執(zhí)行懈糯,比如網(wǎng)絡(luò)下載中斷涤妒,后臺(tái)播放音樂(lè)暫停等。WakeLock正是為了解決這類問(wèn)題赚哗,應(yīng)用只要申請(qǐng)了WakeLock她紫,那么在釋放WakeLock之前硅堆,系統(tǒng)不會(huì)進(jìn)入休眠,即使在滅屏的狀態(tài)下贿讹,應(yīng)用要執(zhí)行的任務(wù)依舊不會(huì)被系統(tǒng)打斷渐逃。

WakeLock有那些分類

WakeLock是PowerManager的內(nèi)部類,其代碼路徑位于:

frameworks/base/core/java/android/os/PowerManager.java

WakeLock 分類如下:

  • PARTIAL_WAKE_LOCK: 滅屏民褂,關(guān)閉鍵盤背光的情況下茄菊,CPU依然保持運(yùn)行。
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK: 基于距離感應(yīng)器熄滅屏幕赊堪。最典型的運(yùn)用場(chǎng)景是我們貼近耳朵打電話時(shí)面殖,屏幕會(huì)自動(dòng)熄滅。
  • SCREEN_DIM_WAKE_LOCK/SCREEN_BRIGHT_WAKE_LOCK/FULL_WAKE_LOCK:這三種WakeLock都已經(jīng)過(guò)時(shí)了雹食,它們的目的是為了保持屏幕長(zhǎng)亮畜普,Android官方建議用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);方式替換。因?yàn)楸绕鹕暾?qǐng)WakeLock,這種方式更簡(jiǎn)單群叶,還不需要特別申請(qǐng)android.permission.WAKE_LOCK權(quán)限吃挑。
  • DOZE_WAKE_LOCK/DRAW_WAKE_LOCK: 隱藏的分類,系統(tǒng)級(jí)別才會(huì)用到街立。

WakeLock的flag如下:

  • ACQUIRE_CAUSES_WAKEUP: 點(diǎn)亮屏幕舶衬,比如應(yīng)用接收到通知后,屏幕亮起。
  • ON_AFTER_RELEASE: 釋放WakeLock后跛锌,屏幕不馬上熄滅舱呻。
  • UNIMPORTANT_FOR_LOGGING: 隱藏的flag,系統(tǒng)級(jí)別才會(huì)用到虽画。

WakeLock的設(shè)置過(guò)程

WakeLock從用戶空間下發(fā)設(shè)置操作,然后進(jìn)入kernel空間荣病,最終寫入到了/sys/power/wake_lock文件節(jié)點(diǎn)码撰。
下面來(lái)從源碼的角度跟蹤下acquire WakeLock的過(guò)程。

  1. frameworks/base/core/java/android/os/PowerManager.java
    acquire--->acquireLocked---->PowerManagerService.acquireWakeLock

  2. frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
    acquireWakeLock--->acquireWakeLockInternal---->updatePowerStateLocked---->updateSuspendBlockerLocked---->mWakeLockSuspendBlocker.acquire---->PowerManagerService$SuspendBlockerImpl.acquire---->nativeAcquireSuspendBlocker

  3. frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
    nativeAcquireSuspendBlocker---->acquire_wake_lock

  4. hardware/libhardware_legacy/power/power.c
    acquire_wake_lock,最終在該方法里將wakelock寫入了節(jié)點(diǎn)

    int 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;
        size_t len;
        ssize_t ret;
    
        if (lock != PARTIAL_WAKE_LOCK) {
            return -EINVAL;
        }
    
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
        //這個(gè)節(jié)點(diǎn)就是/sys/power/wake_lock
        ret = write(fd, id, strlen(id));
        if (ret < 0) {
            return -errno;
        }
    
        return ret;
    }
    

WakeLock用法

WakeLock的使用需要謹(jǐn)慎處理个盆,使用不當(dāng)會(huì)讓應(yīng)用變成“電量殺手”脖岛。使用的原則

  1. 能不用就盡量別用:比如要保持屏幕長(zhǎng)亮,應(yīng)該用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);的方式颊亮。

PowerManager.FULL_WAKE_LOCK會(huì)保持屏幕長(zhǎng)亮柴梆,比如設(shè)置15s自動(dòng)滅屏,當(dāng)申請(qǐng)了該wakelock后终惑,即使超過(guò)15s绍在,依然不會(huì)滅屏。但用戶主動(dòng)按power鍵,還是會(huì)滅屏的揣苏。

  1. 如果非要用悯嗓,用完之后記得釋放。
    申請(qǐng)WakeLock有兩種方式acquire()跟acquire(long timeout)卸察,后者相對(duì)更安全點(diǎn)脯厨,如果忘記了release WakeLock,經(jīng)過(guò)timeout的時(shí)長(zhǎng)后系統(tǒng)會(huì)自動(dòng)release坑质。

WakeLock的典型用法如下:

PowerManager pm = (PowerManager)mContext.getSystemService(
                                          Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
                                      PowerManager.PARTIAL_WAKE_LOCK
                                      | PowerManager.ON_AFTER_RELEASE,
                                      TAG);
wl.acquire();//為了保證任務(wù)不被系統(tǒng)休眠打斷合武,申請(qǐng)WakeLock
// 開(kāi)始我們的任務(wù)
wl.release();//任務(wù)結(jié)束后釋放,如果不寫該句涡扼。則可以用wl.acquire(timeout)的方式把釋放的工作交給系統(tǒng)稼跳。

同時(shí)需要在Manifest文件中添加權(quán)限

<uses-permission android:name="android.permission.WAKE_LOCK."/>

WakeLock相關(guān)問(wèn)題的debug方法

應(yīng)用層debug

如果只是單純的查看某一個(gè)應(yīng)用的wakelock是否存在非正常釋放的情況,可以用命令

$ adb shell dumpsys power|grep -i wake

來(lái)查看吃沪,比如下面的示例代碼

public class MainActivity extends Activity {
    private WakeLock wlLock;
    @Override
    protected void onResume() {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wlLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "azhengye-test-wakelock");
        wlLock.acquire();
        super.onResume();
    }
    @Override
    protected void onPause() {
        wlLock.release();
        super.onPause();
    }
}

當(dāng)進(jìn)入onResume方法汤善,此時(shí)dumpsys的信息如下:

$ adb shell dumpsys power|grep -i wake

  mWakefulness=Awake
  mWakefulnessChanging=false
  mWakeLockSummary=0x1
  mLastWakeTime=34512600 (293075 ms ago)
  mHoldingWakeLockSuspendBlocker=true
  mWakeUpWhenPluggedOrUnpluggedConfig=true
  mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
  mDoubleTapWakeEnabled=false
Wake Locks: size=1
  PARTIAL_WAKE_LOCK              'azhengye-test-wakelock' ON_AFTER_RELEASE ACQ=-4m52s949ms LONG (uid=10124 pid=31473 pkg=com.azhengye.testpath)
  PowerManagerService.WakeLocks: ref count=1

通過(guò) PARTIAL_WAKE_LOCK 'azhengye-test-wakelock' ON_AFTER_RELEASE ACQ=-4m52s949ms LONG (uid=10124 pid=31473 pkg=com.azhengye.testpath) 可以看到wakelock申請(qǐng)成功了。
如果進(jìn)入onPause方法票彪,剛申請(qǐng)的wakelock應(yīng)該被釋放掉红淡,此時(shí)dumpsys出的信息如下:

$ adb shell dumpsys power|grep -i wake

      mWakefulness=Awake
      mWakefulnessChanging=false
      mWakeLockSummary=0x1
      mLastWakeTime=34512600 (477908 ms ago)
      mHoldingWakeLockSuspendBlocker=true
      mWakeUpWhenPluggedOrUnpluggedConfig=true
      mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
      mDoubleTapWakeEnabled=false
    Wake Locks: size=1
      PARTIAL_WAKE_LOCK              'AudioMix' ACQ=-2s539ms (uid=1041 pkg=audioserver)
      PowerManagerService.WakeLocks: ref count=1

沒(méi)毛病,已經(jīng)正常釋放掉降铸。如果應(yīng)用已經(jīng)運(yùn)行到釋放wakelock的語(yǔ)句在旱,但dumpsys出的信息仍然看到持有wakelock,這就是問(wèn)題推掸,需要我們?nèi)ix掉桶蝎。

系統(tǒng)層debug

系統(tǒng)層面去debug wakelock相關(guān)問(wèn)題就比較復(fù)雜了,基本上的步驟如下谅畅。

  1. 依據(jù)log分析
    首先打開(kāi)debug開(kāi)關(guān)
    frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private static final boolean DEBUG = true;
    

    然后dumpsys power查看登渣,同時(shí)在logcat里搜索acquireWakeLockInternal關(guān)鍵字查看申請(qǐng)wakelock情況,
    比如

    08-23 09:45:35.452  3054  3067 D PowerManagerService: acquireWakeLockInternal: lock=1945552, flags=0x1, tag="LocationManagerService", ws=WorkSource{1000 android}, uid=1000, pid=3054, packageName=android
    

    搜索releaseWakeLockInternal查看釋放情況毡泻。lock=后面的整數(shù)能將acquire跟release對(duì)應(yīng)起來(lái)绍豁,在結(jié)合logcat的時(shí)間戳,就能得到wakelock持有的時(shí)長(zhǎng)牙捉,短時(shí)間的一般不會(huì)有問(wèn)題,那種長(zhǎng)期持有wakelock的會(huì)導(dǎo)致功耗增加敬飒,一般是有問(wèn)題的邪铲,需要根據(jù)tag字段去代碼里查看具體的acquire/release過(guò)程。

  2. 系統(tǒng)運(yùn)行一段時(shí)間后抓取bugreport.用historian工具查看wakelock申請(qǐng)情況无拗。具體可以查看之前的博客Android battery historian功耗分析之環(huán)境搭建

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末带到,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子英染,更是在濱河造成了極大的恐慌揽惹,老刑警劉巖被饿,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搪搏,居然都是意外死亡狭握,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門疯溺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)论颅,“玉大人,你說(shuō)我怎么就攤上這事囱嫩∈逊瑁” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵墨闲,是天一觀的道長(zhǎng)今妄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鸳碧,這世上最難降的妖魔是什么盾鳞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮杆兵,結(jié)果婚禮上雁仲,老公的妹妹穿的比我還像新娘。我一直安慰自己琐脏,他們只是感情好攒砖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著日裙,像睡著了一般吹艇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昂拂,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天受神,我揣著相機(jī)與錄音,去河邊找鬼格侯。 笑死鼻听,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的联四。 我是一名探鬼主播撑碴,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼朝墩!你這毒婦竟也來(lái)了醉拓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎亿卤,沒(méi)想到半個(gè)月后愤兵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡排吴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年秆乳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傍念。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矫夷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出憋槐,到底是詐尸還是另有隱情双藕,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布阳仔,位于F島的核電站忧陪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏近范。R本人自食惡果不足惜嘶摊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望评矩。 院中可真熱鬧叶堆,春花似錦、人聲如沸斥杜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蔗喂。三九已至忘渔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缰儿,已是汗流浹背畦粮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乖阵,地道東北人宣赔。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞪浸,于是被迫代替她去往敵國(guó)和親拉背。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容