作者: wenmingvs
Github: https://github.com/wenmingvs/AndroidProcess
用法
傳入Context參數(shù)與想要判斷是否位于前臺的App的包名,會返回ture或者false表示App是否位于前臺
//六種方法任選其一
//使用方法一
Boolean isForeground = BackgroundUtil.getRunningTask(context, packageName);
//使用方法二
Boolean isForeground = BackgroundUtil.getRunningAppProcesses(context, packageName);
//使用方法三
Boolean isForeground = BackgroundUtil.getApplicationValue(context);
//使用方法四
Boolean isForeground = BackgroundUtil.queryUsageStats(context, packageName);
//使用方法五
Boolean isForeground = BackgroundUtil.getFromAccessibilityService(context, packageName);
//使用方法六
Boolean isForeground = BackgroundUtil.getLinuxCoreInfo(context, packageName);
六種方法的區(qū)別
方法 | 判斷原理 | 需要權限 | 可以判斷其他應用位于前臺 | 特點 |
---|---|---|---|---|
方法一 | RunningTask | 否 | Android4.0系列可以,5.0以上機器不行 | 5.0此方法被廢棄 |
方法二 | RunningProcess | 否 | 當App存在后臺常駐的Service時失效 | 無 |
方法三 | ActivityLifecycleCallbacks | 否 | 否 | 簡單有效,代碼最少 |
方法四 | UsageStatsManager | 是 | 是 | 需要用戶手動授權 |
方法五 | 通過Android無障礙功能實現(xiàn) | 否 | 是 | 需要用戶手動授權 |
方法六 | 讀取/proc目錄下的信息 | 否 | 是 | 當proc目錄下文件夾過多時,過多的IO操作會引起耗時 |
方法一:通過RunningTask
原理
當一個App處于前臺的時候已艰,會處于RunningTask的這個棧的棧頂祟滴,所以我們可以取出RunningTask的棧頂?shù)娜蝿者M程刊懈,看他與我們的想要判斷的App的包名是否相同手幢,來達到效果
缺點
getRunningTask方法在Android5.0以上已經被廢棄捷凄,只會返回自己和系統(tǒng)的一些不敏感的task,不再返回其他應用的task围来,用此方法來判斷自身App是否處于后臺跺涤,仍然是有效的,但是無法判斷其他應用是否位于前臺监透,因為不再能獲取信息
方法二:通過RunningProcess
原理
通過runningProcess獲取到一個當前正在運行的進程的List桶错,我們遍歷這個List中的每一個進程,判斷這個進程的一個importance 屬性是否是前臺進程胀蛮,并且包名是否與我們判斷的APP的包名一樣院刁,如果這兩個條件都符合,那么這個App就處于前臺
缺點:
在聊天類型的App中醇滥,常常需要常駐后臺來不間斷的獲取服務器的消息黎比,這就需要我們把Service設置成START_STICKY,kill 后會被重啟(等待5秒左右)來保證Service常駐后臺鸳玩。如果Service設置了這個屬性阅虫,這個App的進程就會被判斷是前臺,代碼上的表現(xiàn)就是appProcess.importance的值永遠是 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND不跟,這樣就永遠無法判斷出到底哪個是前臺了颓帝。
方法三:通過ActivityLifecycleCallbacks
原理
AndroidSDK14在Application類里增加了ActivityLifecycleCallbacks,我們可以通過這個Callback拿到App所有Activity的生命周期回調。
public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity activity, Bundle savedInstanceState);
void onActivityStarted(Activity activity);
void onActivityResumed(Activity activity);
void onActivityPaused(Activity activity);
void onActivityStopped(Activity activity);
void onActivitySaveInstanceState(Activity activity, Bundle outState);
void onActivityDestroyed(Activity activity);
}
知道這些信息购城,我們就可以用更官方的辦法來解決問題吕座,當然還是利用方案二里的Activity生命周期的特性,我們只需要在Application的onCreate()里去注冊上述接口瘪板,然后由Activity回調回來運行狀態(tài)即可吴趴。
可能還有人在糾結,我用back鍵切到后臺和用Home鍵切到后臺侮攀,一樣嗎锣枝?以上方法適用嗎?在Android應用開發(fā)中一般認為back鍵是可以捕獲的兰英,而Home鍵是不能捕獲的(除非修改framework),但是上述方法從Activity生命周期著手解決問題撇叁,雖然這兩種方式的Activity生命周期并不相同,但是二者都會執(zhí)行onStop()畦贸;所以并不關心到底是觸發(fā)了哪個鍵切入后臺的陨闹。另外,Application是否被銷毀,都不會影響判斷的正確性
方法四:通過使用UsageStatsManager獲取
原理
通過使用UsageStatsManager獲取,此方法是Android5.0之后提供的新API薄坏,可以獲取一個時間段內的應用統(tǒng)計信息趋厉,但是必須滿足一下要求
使用前提
1.此方法只在android5.0以上有效
2.AndroidManifest中加入此權限
<uses-permission xmlns:tools="http://schemas.android.com/tools"
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
3.打開手機設置,點擊安全-高級颤殴,在有權查看使用情況的應用中觅廓,為這個App打上勾
方法五:通過Android自帶的無障礙功能
非常感謝@EffectiveMatrix大神帶來的新的判斷前后臺的方法
此方法屬于他原創(chuàng),具體的博文參照這里http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/
此方法無法直觀的通過下拉通知視圖來進行前后臺的觀察涵但,請到LogCat中進行觀察即可,以下是LogCat中打印的信息
原理
Android 輔助功能(AccessibilityService) 為我們提供了一系列的事件回調帖蔓,幫助我們指示一些用戶界面的狀態(tài)變化矮瘟。 我們可以派生輔助功能類,進而對不同的 AccessibilityEvent 進行處理塑娇。 同樣的澈侠,這個服務就可以用來判斷當前的前臺應用
優(yōu)勢
- AccessibilityService 有非常廣泛的 ROM 覆蓋,特別是非國產手機埋酬,從 Android API Level 8(Android 2.2) 到 Android Api Level 23(Android 6.0)
- AccessibilityService 不再需要輪詢的判斷當前的應用是不是在前臺哨啃,系統(tǒng)會在窗口狀態(tài)發(fā)生變化的時候主動回調,耗時和資源消耗都極小
- 不需要權限請求
- 它是一個穩(wěn)定的方法写妥,與 “方法6”讀取 /proc 目錄不同屹堰,它并非利用 Android 一些設計上的漏洞司澎,可以長期使用的可能很大
- 可以用來判斷任意應用甚至 Activity, PopupWindow, Dialog 對象是否處于前臺
劣勢
- 需要要用戶開啟輔助功能
- 輔助功能會伴隨應用被“強行停止”而剝奪
方法六:讀取Linux系統(tǒng)內核保存在/proc目錄下的process進程信息
此方法并非我原創(chuàng),原作者是國外的大神,GitHub項目在這里董习,也一并加入到工程中航缀,供大家做全面的參考選擇
原理
無意中看到烏云上有人提的一個漏洞,Linux系統(tǒng)內核會把process進程信息保存在/proc目錄下,Shell命令去獲取的他酬姆,再根據(jù)進程的屬性判斷是否為前臺
優(yōu)點
- 不需要任何權限
- 可以判斷任意一個應用是否在前臺,而不局限在自身應用
缺點
- 當/proc下文件夾過多時,此方法是耗時操作
用法
獲取一系列正在運行的App的進程
List<AndroidAppProcess> processes = ProcessManager.getRunningAppProcesses();
獲取任一正在運行的App進程的詳細信息
AndroidAppProcess process = processes.get(location);
String processName = process.name;
Stat stat = process.stat();
int pid = stat.getPid();
int parentProcessId = stat.ppid();
long startTime = stat.stime();
int policy = stat.policy();
char state = stat.state();
Statm statm = process.statm();
long totalSizeOfProcess = statm.getSize();
long residentSetSize = statm.getResidentSetSize();
PackageInfo packageInfo = process.getPackageInfo(context, 0);
String appName = packageInfo.applicationInfo.loadLabel(pm).toString();
判斷是否在前臺
if (ProcessManager.isMyProcessInTheForeground()) {
// do stuff
}
獲取一系列正在運行的App進程的詳細信息
List<ActivityManager.RunningAppProcessInfo> processes = ProcessManager.getRunningAppProcessInfo(ctx);