今天,測試提交了一個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,除了這個,也會很有幫助的),如果有知道的,麻煩告知下