本文轉(zhuǎn)轉(zhuǎn)自GitHub https://github.com/wenmingvs/AndroidProcess
AndroidProcess
提供6種方法來(lái)判斷App處于前臺(tái)還是后臺(tái),并且封裝成工具類(lèi)供大家使用
最后一種方法堪稱(chēng)Android黑科技(非原創(chuàng)),既可以突破Android5.0以上的權(quán)限封鎖,獲取任意前臺(tái)App的包名,又不需要權(quán)限,此方法并非我原創(chuàng),原作者是國(guó)外的大神,GitHub項(xiàng)目在這里,也一并加入到工程中,供大家做全面的參考選擇
2016.2.11更新------感謝@EffectiveMatrix大神帶來(lái)的新的判斷前后臺(tái)的方法
快速下載體驗(yàn)
預(yù)覽圖
用法
傳入Context參數(shù)與想要判斷是否位于前臺(tái)的App的包名,會(huì)返回ture或者false表示App是否位于前臺(tái)
//六種方法任選其一
//使用方法一
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ū)別
方法 | 判斷原理 | 需要權(quán)限 | 可以判斷其他應(yīng)用位于前臺(tái) | 特點(diǎn) |
---|---|---|---|---|
方法一 | RunningTask | 否 | Android4.0系列可以,5.0以上機(jī)器不行 | 5.0此方法被廢棄 |
方法二 | RunningProcess | 否 | 當(dāng)App存在后臺(tái)常駐的Service時(shí)失效 | 無(wú) |
方法三 | ActivityLifecycleCallbacks | 否 | 否 | 簡(jiǎn)單有效,代碼最少 |
方法四 | UsageStatsManager | 是 | 是 | 需要用戶手動(dòng)授權(quán) |
方法五 | 通過(guò)Android無(wú)障礙功能實(shí)現(xiàn) | 否 | 是 | 需要用戶手動(dòng)授權(quán) |
方法六 | 讀取/proc目錄下的信息 | 否 | 是 | 當(dāng)proc目錄下文件夾過(guò)多時(shí),過(guò)多的IO操作會(huì)引起耗時(shí) |
方法一:通過(guò)RunningTask
原理
當(dāng)一個(gè)App處于前臺(tái)的時(shí)候,會(huì)處于RunningTask的這個(gè)棧的棧頂,所以我們可以取出RunningTask的棧頂?shù)娜蝿?wù)進(jìn)程,看他與我們的想要判斷的App的包名是否相同,來(lái)達(dá)到效果
缺點(diǎn)
getRunningTask方法在Android5.0以上已經(jīng)被廢棄,只會(huì)返回自己和系統(tǒng)的一些不敏感的task,不再返回其他應(yīng)用的task,用此方法來(lái)判斷自身App是否處于后臺(tái),仍然是有效的,但是無(wú)法判斷其他應(yīng)用是否位于前臺(tái),因?yàn)椴辉倌塬@取信息
方法二:通過(guò)RunningProcess
原理
通過(guò)runningProcess獲取到一個(gè)當(dāng)前正在運(yùn)行的進(jìn)程的List,我們遍歷這個(gè)List中的每一個(gè)進(jìn)程,判斷這個(gè)進(jìn)程的一個(gè)importance 屬性是否是前臺(tái)進(jìn)程,并且包名是否與我們判斷的APP的包名一樣,如果這兩個(gè)條件都符合,那么這個(gè)App就處于前臺(tái)
缺點(diǎn):
在聊天類(lèi)型的App中,常常需要常駐后臺(tái)來(lái)不間斷的獲取服務(wù)器的消息,這就需要我們把Service設(shè)置成START_STICKY,kill 后會(huì)被重啟(等待5秒左右)來(lái)保證Service常駐后臺(tái)。如果Service設(shè)置了這個(gè)屬性,這個(gè)App的進(jìn)程就會(huì)被判斷是前臺(tái),代碼上的表現(xiàn)就是appProcess.importance的值永遠(yuǎn)是 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,這樣就永遠(yuǎn)無(wú)法判斷出到底哪個(gè)是前臺(tái)了。
方法三:通過(guò)ActivityLifecycleCallbacks
原理
AndroidSDK14在Application類(lèi)里增加了ActivityLifecycleCallbacks,我們可以通過(guò)這個(gè)Callback拿到App所有Activity的生命周期回調(diào)。
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);
}
知道這些信息,我們就可以用更官方的辦法來(lái)解決問(wèn)題,當(dāng)然還是利用方案二里的Activity生命周期的特性,我們只需要在Application的onCreate()里去注冊(cè)上述接口,然后由Activity回調(diào)回來(lái)運(yùn)行狀態(tài)即可。
可能還有人在糾結(jié),我用back鍵切到后臺(tái)和用Home鍵切到后臺(tái),一樣嗎?以上方法適用嗎?在Android應(yīng)用開(kāi)發(fā)中一般認(rèn)為back鍵是可以捕獲的,而Home鍵是不能捕獲的(除非修改framework),但是上述方法從Activity生命周期著手解決問(wèn)題,雖然這兩種方式的Activity生命周期并不相同,但是二者都會(huì)執(zhí)行onStop();所以并不關(guān)心到底是觸發(fā)了哪個(gè)鍵切入后臺(tái)的。另外,Application是否被銷(xiāo)毀,都不會(huì)影響判斷的正確性
方法四:通過(guò)使用UsageStatsManager獲取
原理
通過(guò)使用UsageStatsManager獲取,此方法是Android5.0之后提供的新API,可以獲取一個(gè)時(shí)間段內(nèi)的應(yīng)用統(tǒng)計(jì)信息,但是必須滿足一下要求
使用前提
- 此方法只在android5.0以上有效
- AndroidManifest中加入此權(quán)限
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- 打開(kāi)手機(jī)設(shè)置,點(diǎn)擊安全-高級(jí),在有權(quán)查看使用情況的應(yīng)用中,為這個(gè)App打上勾
方法五:通過(guò)Android自帶的無(wú)障礙功能
非常感謝@EffectiveMatrix大神帶來(lái)的新的判斷前后臺(tái)的方法
此方法屬于他原創(chuàng),具體的博文參照這里http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/
此方法無(wú)法直觀的通過(guò)下拉通知視圖來(lái)進(jìn)行前后臺(tái)的觀察,請(qǐng)到LogCat中進(jìn)行觀察即可,以下是LogCat中打印的信息
原理
Android 輔助功能(AccessibilityService) 為我們提供了一系列的事件回調(diào),幫助我們指示一些用戶界面的狀態(tài)變化。 我們可以派生輔助功能類(lèi),進(jìn)而對(duì)不同的 AccessibilityEvent 進(jìn)行處理。 同樣的,這個(gè)服務(wù)就可以用來(lái)判斷當(dāng)前的前臺(tái)應(yīng)用
優(yōu)勢(shì)
- AccessibilityService 有非常廣泛的 ROM 覆蓋,特別是非國(guó)產(chǎn)手機(jī),從 Android API Level 8(Android 2.2) 到 Android Api Level 23(Android 6.0)
- AccessibilityService 不再需要輪詢的判斷當(dāng)前的應(yīng)用是不是在前臺(tái),系統(tǒng)會(huì)在窗口狀態(tài)發(fā)生變化的時(shí)候主動(dòng)回調(diào),耗時(shí)和資源消耗都極小
- 不需要權(quán)限請(qǐng)求
- 它是一個(gè)穩(wěn)定的方法,與 “方法6”讀取 /proc 目錄不同,它并非利用 Android 一些設(shè)計(jì)上的漏洞,可以長(zhǎng)期使用的可能很大
- 可以用來(lái)判斷任意應(yīng)用甚至 Activity, PopupWindow, Dialog 對(duì)象是否處于前臺(tái)
劣勢(shì)
- 需要要用戶開(kāi)啟輔助功能
- 輔助功能會(huì)伴隨應(yīng)用被“強(qiáng)行停止”而剝奪
方法六:讀取Linux系統(tǒng)內(nèi)核保存在/proc目錄下的process進(jìn)程信息
此方法并非我原創(chuàng),原作者是國(guó)外的大神,GitHub項(xiàng)目在這里,也一并加入到工程中,供大家做全面的參考選擇
原理
無(wú)意中看到烏云上有人提的一個(gè)漏洞,Linux系統(tǒng)內(nèi)核會(huì)把process進(jìn)程信息保存在/proc目錄下,Shell命令去獲取的他,再根據(jù)進(jìn)程的屬性判斷是否為前臺(tái)
優(yōu)點(diǎn)
- 不需要任何權(quán)限
- 可以判斷任意一個(gè)應(yīng)用是否在前臺(tái),而不局限在自身應(yīng)用
缺點(diǎn)
- 當(dāng)/proc下文件夾過(guò)多時(shí),此方法是耗時(shí)操作
用法
獲取一系列正在運(yùn)行的App的進(jìn)程
List<AndroidAppProcess> processes = ProcessManager.getRunningAppProcesses();
獲取任一正在運(yùn)行的App進(jìn)程的詳細(xì)信息
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();
判斷是否在前臺(tái)
if (ProcessManager.isMyProcessInTheForeground()) {
// do stuff
}
獲取一系列正在運(yùn)行的App進(jìn)程的詳細(xì)信息
List<ActivityManager.RunningAppProcessInfo> processes = ProcessManager.getRunningAppProcessInfo(ctx);
Gradle 構(gòu)建
- 版本
- 最新 Android SDK
- 最新 Gradle
- 環(huán)境變量
- ANDROID_HOME
- GRADLE_HOME,同時(shí)把bin放入path變量
- Android SDK 安裝,都更新到最新
- Android SDK Build-tools更新到最新
- Google Repository更新到最新
- Android Support Repository更新到最新
- Android Support Library更新到最新
相信未來(lái)
當(dāng)蜘蛛網(wǎng)無(wú)情地查封了我的爐臺(tái)
當(dāng)灰燼的余煙嘆息著貧困的悲哀
我依然固執(zhí)地鋪平失望的灰燼
用美麗的雪花寫(xiě)下:相信未來(lái)
當(dāng)我的紫葡萄化為深秋的露水
當(dāng)我的鮮花依偎在別人的情懷
我依然固執(zhí)地用凝霜的枯藤
在凄涼的大地上寫(xiě)下:相信未來(lái)
我要用手指那涌向天邊的排浪
我要用手掌那托住太陽(yáng)的大海
搖曳著曙光那枝溫暖漂亮的筆桿
用孩子的筆體寫(xiě)下:相信未來(lái)
我之所以堅(jiān)定地相信未來(lái)
是我相信未來(lái)人們的眼睛
她有撥開(kāi)歷史風(fēng)塵的睫毛
她有看透歲月篇章的瞳孔
不管人們對(duì)于我們腐爛的皮肉
那些迷途的惆悵、失敗的苦痛
是寄予感動(dòng)的熱淚、深切的同情
還是給以輕蔑的微笑、辛辣的嘲諷
我堅(jiān)信人們對(duì)于我們的脊骨
那無(wú)數(shù)次的探索、迷途、失敗和成功
一定會(huì)給予熱情、客觀、公正的評(píng)定
是的,我焦急地等待著他們的評(píng)定
朋友,堅(jiān)定地相信未來(lái)吧
相信不屈不撓的努力
相信戰(zhàn)勝死亡的年輕
相信未來(lái)、熱愛(ài)生命