Android 功耗分析之wakelock

生活總是讓我們遍體鱗傷,但到后來,那些受傷的地方一定會變成我們最強壯的地方。---海明威

WakeLock是什么

WakeLock是Android框架層提供的一套機制,應用使用該機制可以達到控制Android設備狀態(tài)的目的。這里的設備狀態(tài)主要指屏幕的打開關閉,cpu的保持運行。簡單的理解WakeLock是讓系統(tǒng)保持"清醒"的一種手段.

WakeLock作用

當手機滅屏狀態(tài)下保持一段時間后,系統(tǒng)會進入休眠,一些后臺運行的任務就可能得不到正常執(zhí)行,比如網(wǎng)絡下載中斷,后臺播放音樂暫停等。WakeLock正是為了解決這類問題,應用只要申請了WakeLock,那么在釋放WakeLock之前,系統(tǒng)不會進入休眠,即使在滅屏的狀態(tài)下,應用要執(zhí)行的任務依舊不會被系統(tǒng)打斷。

WakeLock有那些分類

WakeLock是PowerManager的內部類,其代碼路徑位于:

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

WakeLock 分類如下:

  • PARTIAL_WAKE_LOCK: 滅屏,關閉鍵盤背光的情況下,CPU依然保持運行。
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK: 基于距離感應器熄滅屏幕。最典型的運用場景是我們貼近耳朵打電話時,屏幕會自動熄滅。
  • SCREEN_DIM_WAKE_LOCK/SCREEN_BRIGHT_WAKE_LOCK/FULL_WAKE_LOCK:這三種WakeLock都已經(jīng)過時了,它們的目的是為了保持屏幕長亮,Android官方建議用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);方式替換。因為比起申請WakeLock,這種方式更簡單,還不需要特別申請android.permission.WAKE_LOCK權限。
  • DOZE_WAKE_LOCK/DRAW_WAKE_LOCK: 隱藏的分類,系統(tǒng)級別才會用到。

WakeLock的flag如下:

  • ACQUIRE_CAUSES_WAKEUP: 點亮屏幕,比如應用接收到通知后,屏幕亮起。
  • ON_AFTER_RELEASE: 釋放WakeLock后,屏幕不馬上熄滅。
  • UNIMPORTANT_FOR_LOGGING: 隱藏的flag,系統(tǒng)級別才會用到。

WakeLock的設置過程

WakeLock從用戶空間下發(fā)設置操作,然后進入kernel空間,最終寫入到了/sys/power/wake_lock文件節(jié)點。
下面來從源碼的角度跟蹤下acquire WakeLock的過程。

  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é)點

    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];
        //這個節(jié)點就是/sys/power/wake_lock
        ret = write(fd, id, strlen(id));
        if (ret < 0) {
            return -errno;
        }
    
        return ret;
    }
    

WakeLock用法

WakeLock的使用需要謹慎處理,使用不當會讓應用變成“電量殺手”。使用的原則

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

PowerManager.FULL_WAKE_LOCK會保持屏幕長亮,比如設置15s自動滅屏,當申請了該wakelock后,即使超過15s,依然不會滅屏。但用戶主動按power鍵,還是會滅屏的。

  1. 如果非要用,用完之后記得釋放。
    申請WakeLock有兩種方式acquire()跟acquire(long timeout),后者相對更安全點,如果忘記了release WakeLock,經(jīng)過timeout的時長后系統(tǒ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();//為了保證任務不被系統(tǒng)休眠打斷,申請WakeLock
// 開始我們的任務
wl.release();//任務結束后釋放,如果不寫該句。則可以用wl.acquire(timeout)的方式把釋放的工作交給系統(tǒng)。

同時需要在Manifest文件中添加權限

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

WakeLock相關問題的debug方法

應用層debug

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

$ adb shell dumpsys power|grep -i wake

來查看,比如下面的示例代碼

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();
    }
}

當進入onResume方法,此時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

通過 PARTIAL_WAKE_LOCK 'azhengye-test-wakelock' ON_AFTER_RELEASE ACQ=-4m52s949ms LONG (uid=10124 pid=31473 pkg=com.azhengye.testpath) 可以看到wakelock申請成功了。
如果進入onPause方法,剛申請的wakelock應該被釋放掉,此時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

沒毛病,已經(jīng)正常釋放掉。如果應用已經(jīng)運行到釋放wakelock的語句,但dumpsys出的信息仍然看到持有wakelock,這就是問題,需要我們去fix掉。

系統(tǒng)層debug

系統(tǒng)層面去debug wakelock相關問題就比較復雜了,基本上的步驟如下。

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

    private static final boolean DEBUG = true;
    

    然后dumpsys power查看,同時在logcat里搜索acquireWakeLockInternal關鍵字查看申請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對應起來,在結合logcat的時間戳,就能得到wakelock持有的時長,短時間的一般不會有問題,那種長期持有wakelock的會導致功耗增加,一般是有問題的,需要根據(jù)tag字段去代碼里查看具體的acquire/release過程。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容