Android對App前後臺執行狀態的判斷
在某些特定場景下,我們需要判斷App是否處於後臺執行狀態,常見如:推送或聊天訊息是否需要顯示到通知欄中、設定手勢密碼狀態下應用是否顯示解鎖介面等。
Android系統中判斷App的前後臺執行狀態,大家常用的就是ActivityManager
這個類,獲取RunningAppProcessInfo
或者RunningTaskInfo
這兩種方式,程式碼如下:
/**
* 判斷app是否處於前臺
* @param context
* @return
*/
public static boolean isAppForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Service.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList = activityManager.getRunningAppProcesses();
if (runningAppProcessInfoList==null){
return false;
}
for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) {
if (processInfo.processName.equals(context.getPackageName()) &&
processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
return true;
}
}
return false;
}
和
/**
* 判斷app是否處於前臺
* @param context
* @return
*/
public static boolean isRunningForeground (Context context) {
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
String currentPackageName = cn.getPackageName();
if(!TextUtils.isEmpty(currentPackageName) && currentPackageName.equals(context.getPackageName())) {
return true ;
}
return false ;
}
拋開效能問題不談,這兩種方式看上去貌似沒有問題。但是請注意我用了“貌似”這個詞呢,說明還是有問題的。
通常,我們會在Activity的onStop生命週期方法中判斷應用是否進入後臺。而使用者在進行應用間切換、按HOME鍵進入桌面、按開機鍵進入鎖屏狀態會影響到應用的前後臺執行狀態。通過Debug可以看到,切換到其他應用(按HOME鍵也是切換到其他應用)時,這兩種方法返回false,表明應用進入後臺執行狀態,但按關機鍵進入鎖屏狀態時,返回true,也就是說App依然處於前臺執行狀態。拿手勢密碼案例來說,如果使用者按開機鍵進入鎖屏狀態,當再次按開機鍵回到應用時,當然希望應用之前也算進入後臺狀態而顯示解鎖介面,顯然,上面兩種方法已經不滿足我們的需求了。
那麼有沒有一種更好的方式也能監聽到使用者使用開機鍵進入手機鎖屏狀態下呢?答案當然是有!通過Activity的生命週期方法我們可以實現這樣的功能。
我們知道,如果應用處於前臺,當前Activity一定呼叫了onResume方法,而應用進入後臺,當前Activity一定呼叫了onStop方法。根據這個原理,如果我們使用一個計數變數activityCount,初始化為0,在我們自定義的所有Activity基類BaseActivity對應的生命週期方法中做加減計數處理,那麼,當activityCount為1時,則表示應用處於前臺執行狀態,反之,為0時表示應用處於後臺執行狀態。
利用Activity生命週期來判斷應用的前後臺執行狀態,很輕鬆的獲取了各種情境下應用的前後臺狀態變換。但是有個細節需要注意,activityCount的加減處理應該加在Activity的哪個生命週期方法裡面。
很多朋友會不假思索的選擇在onPause方法裡面減一,在onResume方法裡面加一。這種情況下,會出現一個短暫的間隔問題。
比如從Activity A跳轉至Activity B,A和B的生命週期變化經歷瞭如下一個過程:
A onPause ——> B onCreate ——> B onStart ——> B onResume ——> A onStop
可以看出,在A onPause到 B onResume之間,根據我們對activityCount的判斷,表示在此短暫的階段應用處於後臺執行狀態,而實際情況是前臺執行狀態,顯然是有問題的。
所以,正確的方式應該是在onStart裡面對activityCount加一,在onStop裡面對Activity減一,而這個階段的時間小到可以忽略不計。
上述這種間隔問題主要考慮到諸如訊息推送這場景下的一個應用前後臺狀態的判斷,而在手勢密碼這種使用場景下,無需考慮,也可以在其他生命週期方法中處理,只需要在onResume方法中判斷是否需要展示解鎖解介面。
補充:應用進入後臺再回到前臺時,當前Activity生命週期的變化過程如下,通常往往容易忽視在onRestart之後的onStart方法:
onPause ——> onStop ——> onRestart ——> onStart ——> onResume