wakelock使用過度的問題。

今天,測試提交了一個bug,說有個任務(wù)執(zhí)行一半沒了。(因為任務(wù)是由一個不可關(guān)閉的dialog控制,換句話說,dialog意外關(guān)閉了),差看了下log,細(xì)心的發(fā)現(xiàn)了一條ActivityManager發(fā)出來的log

Killing 10190:com.xxx.xxx.xxx/u0a59 (adj 7):excessive wake held 900036 during 900036

很費解,這是系統(tǒng)殺的,系統(tǒng)為啥殺呢?從字面意思看好像是過度使用了wake(因為dialog有個倒計時,所以,使用了wakelock鎖)
那就從系統(tǒng)源碼上看吧,ActivityManager發(fā)出的信息是在ActivityManagerService里執(zhí)行的,直接打開該文件,搜索,直接到下面的函數(shù)(去掉部分和本次無關(guān)的代碼)

//這個是檢查是否過度使用wakelock以及cpu(cpu部分,有興趣的自己看源碼),如果過渡使用,則干掉
//這個檢查函數(shù)是每隔15分鐘檢查一次的,從開機的時候開始。
final void checkExcessivePowerUsageLocked(boolean doKills) {
        updateCpuStatsNow();

        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();  //wakelock一些信息是在BatteryStats里保存的
        boolean doWakeKills = doKills;  //剛開始是true,也就是默認(rèn)要干掉
        boolean doCpuKills = doKills;
        if (mLastPowerCheckRealtime == 0) {
            doWakeKills = false;  //這個是上次檢查的時間,如果是0,則表示這是第一次檢查,則不會干掉任何進(jìn)程(開機完成即啟用第一次檢查)
        }
       //.....
        if (stats.isScreenOn()) {
            doWakeKills = false;//如果屏幕開啟的時候也不會干掉,所以這個是為了防止后臺的。
        }
        final long curRealtime = SystemClock.elapsedRealtime();
        final long realtimeSince = curRealtime - mLastPowerCheckRealtime;  //從上次檢查到現(xiàn)在的時間
        final long curUptime = SystemClock.uptimeMillis();
        final long uptimeSince = curUptime - mLastPowerCheckUptime;
        mLastPowerCheckRealtime = curRealtime;
        mLastPowerCheckUptime = curUptime;
        if (realtimeSince < WAKE_LOCK_MIN_CHECK_DURATION) {
            doWakeKills = false;  //如果檢查時間小于5分鐘,則不殺進(jìn)程,WAKE_LOCK_MIN_CHECK_DURATION是5分鐘,可以自己看下定義
        }
        if (uptimeSince < CPU_MIN_CHECK_DURATION) {
            doCpuKills = false;
        }
        int i = mLruProcesses.size();
        while (i > 0) {  //遍歷所有進(jìn)程
            i--;
            ProcessRecord app = mLruProcesses.get(i);
            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                //對于setProcState>=PROCESS_STATE_HOME生效,這個定義,每個sdk可能不一樣,大概是狀態(tài)是在后臺或者更低級別(數(shù)值越小,優(yōu)先級越高,換句話說,如果當(dāng)前正在顯示或者是在前臺的,即使關(guān)閉屏幕也不會被殺掉,所以,測試條件一定是返回桌面,并且關(guān)閉屏幕)
                long wtime;
                synchronized (stats) {
                    wtime = stats.getProcessWakeTime(app.info.uid,
                            app.pid, curRealtime);  //根據(jù)進(jìn)程的uid和pid查找上次使用wakelock的時間,便于計算使用wakelock時間
                }
                long wtimeUsed = wtime - app.lastWakeTime;
                long cputimeUsed = app.curCpuTime - app.lastCpuTime;

                //其實這句話已經(jīng)解釋得很清楚了,一個進(jìn)程如果擁有wakelock超過他總長時間的一半,就直接殺掉。
                //加上前面的判斷,如果一個進(jìn)程在后臺,并且屏幕關(guān)閉的條件下,如果存在時間超過5分鐘,并且,使用wakelock的時間超過總時間的一半,則殺掉)
                // If a process has held a wake lock for more
                // than 50% of the time during this period,
                // that sounds bad.  Kill!
                if (doWakeKills && realtimeSince > 0
                        && ((wtimeUsed*100)/realtimeSince) >= 50) {
                    synchronized (stats) {
                        stats.reportExcessiveWakeLocked(app.info.uid, app.processName,
                                realtimeSince, wtimeUsed);
                    }
                    //這個就是殺進(jìn)程,也是通過這句log查找到這兒的。
                    app.kill("excessive wake held " + wtimeUsed + " during " + realtimeSince, true);
                    app.baseProcessTracker.reportExcessiveWake(app.pkgList);
                } 
                //......其他的判斷,其中cpu是使用超過四分一,有興趣自己看。
            }
        }
    }

ok,殺進(jìn)程的函數(shù)已經(jīng)找到了,那么,什么時候調(diào)用的呢?

final class MainHandler extends Handler {
        public MainHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
           //other case
            case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
                synchronized (ActivityManagerService.this) {
                    //調(diào)用剛才分析的函數(shù),并且,默認(rèn)的kill操作是true的
                    checkExcessivePowerUsageLocked(true);
                    removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
                    Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);  //繼續(xù)發(fā)送同一個信息,也就是定時檢查,檢查間隔是POWER_CHECK_DELAY,15分鐘
                    sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                }
            } break;
            //other cases
        }
    };

ok,很簡單,就是一個handler處理機制,并且15分鐘定時檢查,那么第一條信息是誰發(fā)送的呢?繼續(xù)查.

final void finishBooting() {
        //other code

        synchronized (this) {
           //other code

            //這里的mFactoryTest 是不等于FactoryTest.FACTORY_TEST_LOW_LEVEL的,待會兒說
            if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                // Start looking for apps that are abusing wake locks.
                //這里發(fā)送第一條信息
                Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
                mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
               //  other code
        }
    }

這里的函數(shù),從名字看就知道是啟動完成后,剩下的就不繼續(xù)分析了,有興趣的可以自己再去看下,不過,估計要從開機啟動開始分析了。
接下來分析下mFactoryTest這個變量,全局搜索下,就在AMS的構(gòu)造函數(shù)有賦值

 public ActivityManagerService(Context systemContext) {
        mContext = systemContext;
        mFactoryTest = FactoryTest.getMode();
        mSystemThread = ActivityThread.currentActivityThread();

        Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
        //other code
}

FactoryTest代碼太簡單了,直接附上代碼得了

public final class FactoryTest {
    public static final int FACTORY_TEST_OFF = 0;
    public static final int FACTORY_TEST_LOW_LEVEL = 1;
    public static final int FACTORY_TEST_HIGH_LEVEL = 2;

    /**
     * Gets the current factory test mode.
     *
     * @return One of: {@link #FACTORY_TEST_OFF}, {@link #FACTORY_TEST_LOW_LEVEL},
     * or {@link #FACTORY_TEST_HIGH_LEVEL}.
     */
    public static int getMode() {
        //默認(rèn)是FACTORY_TEST_OFF(0),而不是FACTORY_TEST_LOW_LEVEL(1),正常的機子都是工廠測試都是關(guān)閉的,換句話說,如果是工廠測試的機子,是不會過渡使用的。
        //如果硬是要看的話,可以到以下幾個目錄查看是否具有該值
        /default.prop
        /system/build.prop
        /system/default.prop
        /data/local.prop
        /data/property目錄下所有presist屬性
        return SystemProperties.getInt("ro.factorytest", FACTORY_TEST_OFF);
    }

    /**
     * When true, long-press on power should immediately cause the device to
     * shut down, without prompting the user.
     */
    public static boolean isLongPressOnPowerOffEnabled() {
        return SystemProperties.getInt("factory.long_press_power_off", 0) != 0;
    }
}

好了,分析以上的原因就是說下,wakelock雖然好用,但是也不能過度使用,使用的時候先確認(rèn)下是否真的需要,以及cpu(這個就要避免一直在后臺頻繁操作了),最后,15分鐘檢查一次的,如果看了源碼會發(fā)現(xiàn),如果調(diào)試態(tài)是可以縮短的,目前不知道在不動用底層的前提下,如何開啟ams調(diào)試態(tài)(開了調(diào)試態(tài),多了很多l(xiāng)og,除了這個,也會很有幫助的),如果有知道的,麻煩告知下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1:InputChannel提供函數(shù)創(chuàng)建底層的Pipe對象 2: 1)客戶端需要新建窗口 2)new ViewRo...
    自由人是工程師閱讀 5,715評論 0 18
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,034評論 25 709
  • ANR問題,相信是每位開發(fā)日常都會遇到的問題,對于這類問題的分析,按照官方的推薦,或網(wǎng)絡(luò)博客的總結(jié)思路能解決一定的...
    tiger桂閱讀 18,281評論 5 28
  • 想不動聲色 但腦子里有情緒和扭曲的五官 想溫文儒雅 但骨子里有叛逆和好動的四肢 想通情達(dá)理 但生活里有無理和蠻橫的...
    煩人日記閱讀 323評論 0 1
  • 近來迷戀聽異域風(fēng)的小語種音樂,尤其是泰語歌,好像泰國人有種神奇的天賦,能把生就的熱情和開放融進(jìn)歌曲里,唱天空唱流水...
    一枕閱讀 991評論 0 2

友情鏈接更多精彩內(nèi)容