Android獲取前臺進程技術方案

Andoid系統從Android5.0開始對獲取前臺進程接口進行相關限制。本文爲對突破Android接口限制進行的一系列研究的總結。目前所有獲取前臺進程的接口有如下7種方式:

接下來將對每一種方案進行詳細的闡述。

1.   通過RunningTask

1.1.  實現原理

當一個App處於前臺的時候,會處於RunningTask的這個棧的棧頂,所以我們可以取出RunningTask的棧頂的任務進程,看他與我們的想要判斷的App的包名是否相同,來達到效果。

代碼實現如下:

1.2.  方案缺點

getRunningTask方法在Android5.0以上已經被廢棄,只會返回自己和系統的一些不敏感的task,不再返回其他應用的task,用此方法來判斷自身App是否處於後臺,仍然是有效的,但是無法判斷其他應用是否位於前臺,因爲不再能獲取信息

2.   通過RunningProcess

2.1.  實現原理

通過runningProcess獲取到一個當前正在運行的進程的List,我們遍歷這個List中的每一個進程,判斷這個進程的一個importance 屬性是否是前臺進程,並且包名是否與我們判斷的APP的包名一樣,如果這兩個條件都符合,那麼這個App就處於前臺。

代碼實現如下:

2.2.  方案缺點

1、  對於前臺Service(通過setForeground接口設置)的進程會被誤判判斷是前臺進程,代碼上的表現就是appProcess.importance的值永遠是ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,這樣就會存在誤判的情況出現。

2、  該方案只在Android5.0之前有效,Android5.0以後也被系統廢棄。而且基於Android5.0的小米開發版及部分基於Android5.0的華爲版本該方案也會存在問題。

針對會在部分Android5.0的機型上存在問題的問題,我封裝了一個測試接口用於判斷,如果該接口返回爲true則說明爲問題機型,則RunningProcess方案不適用。測試接口的思想爲:先通過getRunningAppProcesses獲取到進程的List,然後判斷如果進程數量非常少(這裏設定的閾值爲3個),或者進程列表中只有Launcher和應用自身存在,則認爲接口存在問題。測試代碼如下:

 

3.   通過ActivityLifecycleCallbacks

3.1.  實現原理

AndroidSDK14Application類裏增加了ActivityLifecycleCallbacks,我們可以通過這個Callback拿到App所有Activity的生命週期回調。

public interface ActivityLifecycleCallbacks {

voidonActivityCreated(Activity activity, Bundle savedInstanceState);

voidonActivityStarted(Activity activity);

voidonActivityResumed(Activity activity);

voidonActivityPaused(Activity activity);

voidonActivityStopped(Activity activity);

voidonActivitySaveInstanceState(Activity activity, Bundle outState);

voidonActivityDestroyed(Activity activity);

    }

知道這些信息,我們就可以用更官方的辦法來解決問題,當然還是利用方案二里的Activity生命週期的特性,我們只需要在ApplicationonCreate()裏去註冊上述接口,然後由Activity回調回來運行狀態即可。

3.2.  方案特點

1、  Android應用開發中一般認爲back鍵是可以捕獲的,而Home鍵是不能捕獲的(除非修改framework,雖然這兩種方式的Activity生命週期並不相同,但是二者都會執行onStop();所以並不關心到底是觸發了哪個鍵切入後臺的。另外,Application是否被銷燬,都不會影響判斷的正確性。

2、  該方案除了用於判斷當前應用內那個Activity位於前臺外,還可以用於作爲實現“進程完全退出”的一種很好的技術方案。

3、  該方案需要在Application中進行註冊相關Activity生命週期的回調,主要代碼如下:

在用該接口判斷應用是否在前臺時,只需要對activityCount計數進行判斷即可:

4.   通過UsageStatsManager獲取

4.1.  實現原理

通過使用UsageStatsManager獲取,此方法是Android5.0之後提供的新API,可以獲取一個時間段內的應用統計信息,但是必須滿足一下要求

使用前提

1、此方法只在android5.0以上有效

2AndroidManifest中加入此權限

3、手動開啓權限:打開手機設置,點擊安全-高級,在有權查看使用情況的應用中,爲這個App打上勾

該方案的實現代碼如下:

 

4.2.  方案特點

1、  該方案最大的缺點是需要用戶手動授權,因此在使用時要結合場景做適當引導。

2、  該方案爲Android5.0以後Google官方比較推薦的獲取進程信息的方式,是最符合Google意圖的方式,不過在使用時會有一些延時需要小心處理。

4.3.  相關輔助接口

1、跳轉到查看應用使用權限界面的跳轉代碼如下:

2、判斷查看應用使用權限是否開啓的接口:

 

5.   通過AccessibilityService獲取

5.1.  技術原理

Android 輔助功能(AccessibilityService) 爲我們提供了一系列的事件回調,幫助我們指示一些用戶界面的狀態變化。我們可以派生輔助功能類,進而對不同的AccessibilityEvent進行處理。同樣的,這個服務就可以用來判斷當前的前臺應用

5.2.  方案特點

1. AccessibilityService有非常廣泛的 ROM 覆蓋,特別是非國產手機,從 API Level 8(Android 2.2) API Level 23(Android 6.0)

2. AccessibilityService不再需要輪詢的判斷當前的應用是不是在前臺,系統會在窗口狀態發生變化的時候主動回調,耗時和資源消耗都極小。

3. 不需要權限請求

4. 它是一個穩定的方法,並非利用 Android 一些設計上的漏洞,可以長期使用的可能很大。

5. 可以用來判斷任意應用甚至 ActivityPopupWindow Dialog 對象是否處於前臺。

5.3.  方案缺點

1、  需要用戶手動開啓輔助功能。

2、  輔助功能會伴隨應用被“強行停止”或第三方管理工具通過Root而剝奪,而且進程重啓後需要對用戶進行重新引導開啓。

3、  部分廠商可能對輔助功能進行限制,如已發現的vivo的部分機型

5.4.  方案實現

1、派生 ACCESSIBILITY SERVICE,創建窗口狀態探測服務:

 

4、  創建 ACCESSIBILITY SERVICE INFO 屬性文件:

 

5、  AndroidManifes.xml中註冊第1步定義的Service

6、  使用服務判斷應用是否在前臺:

5.5.  相關輔助接口

1、  跳轉到“輔助功能”設置界面的代碼如下:

2、判斷輔助功能是否開啓的代碼如下:

6.   自解析/process獲取

6.1.  技術原理

Linux系統內核會把process進程信息保存在/proc目錄下,通過JNI封裝接口,在JNI中去訪問並解析相關進程的設備節點,再根據進程的屬性判斷是否爲前臺

6.2.  方案特點

1、不需要任何權限。

2、可以判斷任意一個應用是否在前臺,而不侷限在自身應用。

3、當/proc下文件夾過多時,此方法是耗時操作。

4、該方案存在能耗問題。

5、在Android6.0.1以上版本或部分廠商版本受限於SEAndroid,只能獲取到第三方進程的信息。

6.3.  代碼實現

Android層我們平時使用較多的進程屬性包括:piduidprocessNamepackageNameimportance等,在自解析方案中只對這些屬性進行解析就可以滿足大部分需求。

該方案中需要進行解析的設備節點包括:

6.4.  代碼缺點

1、  該方案在6.0手機適配運行OK,但在最新的小米、華爲6.0.1手機中發現受SELinux的限制,無法讀取系統應用的設備節點進行解析,只能解析第三方應用設備節點。

2、  可能存在能耗問題。

6.5.  能耗問題解決

1、  Java層對象緩存:對調用比較頻繁的Java層對象在JNI中建立全局緩存,這就避免了每次調用時都需要通過JNI接口獲取。

對一些判斷是需要的場景在初始化時有Java層傳入Jni層,並建立全局緩存:

2、  過來爲Android進程:將pid小於1000Native進程過濾掉。

3、  只解析發生變化的進程:在每次輪詢解析/proc節點時先判斷進程的pid在緩存中是否存在,如果存在只需要更新進程的優先級信息,其他信息不會發生變化;如果進程之前不存在則需要全新解析:

命中緩存時的解析代碼如下:

未命中緩存時,則進全新解析:

4、  在解析進程時,過來父進程爲zygote的進程:Android中所有應用進程的父進程都是zyote

5、  Java層對調用做緩存處理:對於調用比較頻繁的情況,如果當次Native調用沒有完成,則之前返回之前的值,不需要堵塞等待。

6、  對於只關心前臺進程的場景進行特殊處理:

通過優化後,適配方案的能耗與系統接口基本保持一致:

 

 

7.   作爲系統進程的獲取方式

7.1.  技術方案

雖然getRunningTaskAndroid5.0開始被系統廢棄,但是作爲系統應用時,該接口依然是可用的。在用戶取得Root權限,或者應用跟廠商進行合作時,應用本身可能會被內置的系統目錄,即:/system/app/system/private-app目錄。因此對於這種情況,使用getRunningTask獲取依然是一種方便的實現。

7.2.  方案實現

1、  需要判斷應用是否爲系統應用:

2、  AndroidManifest.xml中需要聲明如下權限:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章