檢測Android的Activity任務棧

Android對於Activity有嚴格的生命週期控制,以限制開發者在適當的回調函數裏的放上合適的代碼。對於多個Activity的轉換,Android也有非常好的管理和流暢的切換,對此Android還引入了任務棧(Task Stack)的概念,這個概念對於Android設備上得返回按鍵有極其重要的聯繫。(大部分文檔都將其表述爲Tasks and Back Stack,但從官方文檔的描述來看,Android的相對於Activity講到的Task都視爲一個存放Activities的Stack,所以將其稱爲Task Stack也不爲過。)

在AndroidManifest中申明所要用到的Activity時可以設置不同的launchMode來得到不同的Activity“啓動”效果。在使用startActivity開啓新的Activity時,傳入的Intent也可以設置不同的Flag來達到不同的效果。另一方面,在Activity啓動時它可能又開啓了另一個Activity,或者調用了finish()函數終結了Activity。這使得Activity棧變得無法掌握,有時候按下返回按鈕或者點擊關閉當前Activity的操作,都不知道Android系統會把程序帶到那個Activity,不確定這是否是最後一個Activity以致退出了整個程序。亦或者一些按鈕和操作循環產生Activity而造成內存膨脹。對於這些問題,如果能夠在調試期間知道當前任務棧的情況,就能很方便的觀察和發現問題存在的原因,進而選擇正確的launchMode,設置恰當Intent的Flag來使程序達到預期的效果。

有兩個方法可以獲得任務棧的情況:1.通過代碼調用ActivityManager獲取任務棧信息。2. 代碼中手動記錄和管理Activities棧。3.使用adb shell指令獲得電腦調試的手機啓動的應用的任務棧信息。

一、 通過代碼調用ActivityManager獲取任務棧信息。

Android提供了ActivityManger來幫助開發者瞭解運行期間的狀態,通過調用getRunningTasks(int)方法,就可以在得到RunningTaskInfo的列表,其代表着當前Android設備正在運行着的Task。從RunningTaskInfo中又可以進一步得到更多的信息。

ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);

List<RunningTaskInfo> runningTaskInfoList = am.getRunningTasks(10);

for (RunningTaskInfo runningTaskInfo : runningTaskInfoList) {

    log("id: " + runningTaskInfo.id);

    log("description: " + runningTaskInfo.description);

    log("number of activities: " + runningTaskInfo.numActivities);

    log("topActivity: " + runningTaskInfo.topActivity);

    log("baseActivity: " + runningTaskInfo.baseActivity.toString());

}

在onCreate()方法上放置了上述代碼,就可以觀察觀察log查看當前有多少個Task在被執行,每個Task又有多少個Activities。

缺點:

必須在程序中注入調試代碼,因爲要控制在發佈時代碼必須被清理了。RunningTaskInfo雖然能夠告訴我們有多少個Activity保存在其上,但是沒有提供完整的列表,只能看到頭尾兩個Activity。給出的兩個Activity的屬性:topActivity和baseActivity也只是ComponentName類型,並非真實的Activity對象,因此除了類的名字沒有其他更多信息。

二、代碼手動記錄和管理Activities棧

Activity的創建和銷燬都會有相應的回調函數:onCreate(),onDestroy()。因此可以自建一個靜態全局Stack對象,在onCreate()時候講當前Activity對象加入到Stack中,而在onDestroy()時把它從Stack中移除。這樣我們就隨時可以知道當前Activity的詳細情況了。

缺點:

要讓所有Activity的onCreate()和onDestroy()方法上有對應的進出棧的方法,要麼有統一的基類,要麼強制每個Activity都加入這些代碼,但兩種方式都不完美。另外也很難模擬singleTask這類會創建出新的Task的情況,這時光使用一個Stack就不足夠了,要考慮所有的情況又不太可能。再者如同使用ActivityManager一樣這些代碼也應該只出現在調試階段

三、使用adb shell指令獲得電腦調試的手機啓動的應用的任務棧信息

Android還爲開發者提供了adb(Android Debug Bridge),這是非常強大的調試工具。最常用的自然是logcat來顯示日誌記錄。另外一個很強大的指令就是這裏要提到的dumpsys。dumpsys還可以添加不同的參數來指示需要輸出哪一類Service的信息。對於本文提到的內容,需要查看的就是activity,指令就是:

adb shell dumpsys activity 或 adb shell dumpsys activity activities

輸入上述指令,就能得到關於設備非常長的一段訊息,整個log顯示了當前所有在運行的任務棧,它們的id分別是什麼。對於每個Task,也有Activity數量等信息,同時也列出了其中的Activity列表,並且對於每個Activity也有比較詳細的描述,比如啓動它的Intent的內容,如果覺得內容過多,只想看看棧的內容,也可以直接跳到”Running activities (most recent first)”那部分,比較簡潔而又明瞭的列出了棧中得Activity列表,就能知道當按下返回鍵的時候會應該會回到哪個Activity以後是要退出程序,比如:

    Running activities (most recent first):
      TaskRecord{c4b1703 #13459 A=com.xiaoniu.finance U=0 StackId=1 sz=4}
        Run #6: ActivityRecord{5f4fa1d u0 com.eaway.test/.ui.WebActivity t13459}
        Run #5: ActivityRecord{aa81422 u0 comeaway.test/.ui.invest.h.c t13459}
      TaskRecord{b60fb21 #13462 A=com.bluefin.money U=0 StackId=1 sz=1}
        Run #4: ActivityRecord{1552b46 u0 com.eaway.test/.ui.main.MainActivity t13462}
      TaskRecord{a80ef44 #13461 A=com.elephant.live U=0 StackId=1 sz=1}
        Run #3: ActivityRecord{f10de1a u0 com.elephant.live/net.wequick.small.A t13461}
      TaskRecord{374adbc #13460 A=android.task.calendar U=0 StackId=1 sz=1}
        Run #2: ActivityRecord{1057145 u0 com.android.calendar/.AllInOneActivity t13460}
      TaskRecord{c4b1703 #13459 A=com.xiaoniu.finance U=0 StackId=1 sz=4}
        Run #1: ActivityRecord{c4de680 u0 com.eaway.test/.ui.q t13459}
      TaskRecord{42a6600 #13455 I=com.android.settings/.Settings$WifiSettingsActivity U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{5219e39 u0 com.android.settings/.Settings$WifiSettingsActivity t13455}

    mResumedActivity: ActivityRecord{5f4fa1d u0 com.eaway.test/.ui.WebActivity t13459}

缺點

很明顯的看出,使用adb shell的相對於之前的方式的明顯好處就是不需要添加額外的代碼,而且任務棧的信息也更加詳盡。但是同樣的它只能輸出Activity的類名,對於具體屬性沒有記錄。

 

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