使用adb shell dumpsys檢測Android的Activity任務棧

來源:互聯網
上載者:User

標籤:

談起Android程式開發,就需要瞭解其四個主要的組件:Activity、Service、ContentProvider、 BroadcastReceiver。而其中Activity是唯一直接控製程序介面呈現,直面使用者操作的組件(當然BrowadCastReceiver也能通過案頭控制項(App Widgets)來呈現有限的操作介面)。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,設定恰當IntentFlag來使程式達到預期的效果。

 

通過ActivityManager擷取狀態

Android提供了ActivityManger來協助開發人員瞭解運行期間的狀態,通過調用getRunningTasks(int)方法,就可以在得到RunningTaskInfo的列表,其代表著當前Android裝置正在運行著的Task。從RunningTaskInfo中又可以進一步得到更多的資訊。

  1. ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
  2. List<RunningTaskInfo> runningTaskInfoList = am.getRunningTasks(10);
  3. for (RunningTaskInfo runningTaskInfo : runningTaskInfoList) {
  4. log("id: " + runningTaskInfo.id);
  5. log("description: " + runningTaskInfo.description);
  6. log("number of activities: " + runningTaskInfo.numActivities);
  7. log("topActivity: " + runningTaskInfo.topActivity);
  8. log("baseActivity: " + runningTaskInfo.baseActivity.toString());
  9. }

例如文中提供的樣本程式中定義了4個具有不同launchMode的Activity,每點擊一次功能表列上得選項就會彈出一個新的Activity(或者將指定Singleton的Activity置前)。

Activity上顯示的數字則指示startActivity()被第幾次調用時開啟了這個Activity。有一些Singleton的會顯示多個數字,也表明它是被複用的。

因為在onCreate()方法上放置了上述代碼,所以觀察log就能發現當前有多少個Task在被執行,每個Task又有多少個Activities。

D/IDER    ( 3700): =====================D/IDER    ( 3700): ---------------------D/IDER    ( 3700): id: 25D/IDER    ( 3700): description: nullD/IDER    ( 3700): number of activities: 4D/IDER    ( 3700): topActivity: ComponentInfo{com.iderzheng/com.iderzheng.StandardActivity}D/IDER    ( 3700): baseActivity: ComponentInfo{com.iderzheng/com.iderzheng.SingleTaskActivity}D/IDER    ( 3700): ---------------------D/IDER    ( 3700): id: 24D/IDER    ( 3700): description: nullD/IDER    ( 3700): number of activities: 1D/IDER    ( 3700): topActivity: ComponentInfo{com.iderzheng/com.iderzheng.SingleInstanceActivity}D/IDER    ( 3700): baseActivity: ComponentInfo{com.iderzheng/com.iderzheng.SingleInstanceActivity}D/IDER    ( 3700): ---------------------D/IDER    ( 3700): id: 23D/IDER    ( 3700): description: nullD/IDER    ( 3700): number of activities: 2D/IDER    ( 3700): topActivity: ComponentInfo{com.iderzheng/com.iderzheng.StandardActivity}D/IDER    ( 3700): baseActivity: ComponentInfo{com.iderzheng/com.iderzheng.MainActivity}D/IDER    ( 3700): ---------------------D/IDER    ( 3700): id: 1D/IDER    ( 3700): description: nullD/IDER    ( 3700): number of activities: 1D/IDER    ( 3700): topActivity: ComponentInfo{com.android.launcher/com.android.launcher2.Launcher}D/IDER    ( 3700): baseActivity: ComponentInfo{com.android.launcher/com.android.launcher2.Launcher}
缺點

必須在程式中注入調試代碼,因為要控制在發布時代碼必須被清理了。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來顯示日誌記錄。另外一個很強大的指令就是這裡要提到的dumpsysdumpsys還可以添加不同的參數來指示需要輸出哪一類Service的資訊。對於本文提到的內容,需要查看的就是activity,指令就是:

adb shell dumpsys activity


輸入上述指令,就能得到關於裝置非常長的一段訊息,單是也能清晰看出它們比較詳細的分類

 

ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)  * PendingIntentRecord{42b05f20 com.android.vending startService}  ... ... ... ...ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)  Historical broadcasts [foreground]:  #0: BroadcastRecord{430d2fb8 u-1 android.intent.action.TIME_TICK}    act=android.intent.action.TIME_TICK flg=0x50000014 (has extras)    extras: Bundle[{android.intent.extra.ALARM_COUNT=1}]  ... ... ... ...ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)  Published single-user content providers (by class):  * ContentProviderRecord{429d18a8 u0 com.android.phone/.IccProvider}    proc=ProcessRecord{429765d8 858:com.android.phone/1001}    singleton=true    authority=icc  ... ... ... ...ACTIVITY MANAGER SERVICES (dumpsys activity services)  User 0 active services:  * ServiceRecord{429f8668 u0 com.android.bluetooth/.hid.HidService}    app=null    created=-1h44m27s317ms started=false connections=0  ... ... ... ...ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)  Stack #0:    Task id #28      TaskRecord{43525058 #28 A=com.android.systemui U=0 sz=1}      Intent { act=com.android.systemui.recent.action.TOGGLE_RECENTS flg=0x10c00000 cmp=com.android.systemui/.recent.RecentsActivity (has extras) }        Hist #0: ActivityRecord{428d1ae8 u0 com.android.systemui/.recent.RecentsActivity t28}          Intent { act=com.android.systemui.recent.action.TOGGLE_RECENTS flg=0x10800000 cmp=com.android.systemui/.recent.RecentsActivity bnds=[328,886][656,1176] }          ProcessRecord{42968230 695:com.android.systemui/u0a12}  ... ... ... ...ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)  Process LRU list (sorted by oom_adj, 28 total, non-act at 3, non-svc at 3):    PERS #27: sys   F/ /P  trm: 0 605:system/1000 (fixed)  ... ... ... ...

每一個類別都有一個括弧內容,給出了更加詳細的指令來查看該類別下更多具體內容。因此再來嘗試指令:

db shell dumpsys activity activities

就能看到下邊的結果

CTIVITY MANAGER ACTIVITIES (dumpsys activity activities)  Stack #0:    Task id #28    * TaskRecord{43525058 #28 A=com.android.systemui U=0 sz=1}      ... ... ... ...      * Hist #0: ActivityRecord{428d1ae8 u0 com.android.systemui/.recent.RecentsActivity t28}          ... ... ... ...    Task id #1    * TaskRecord{429a35f8 #1 A=com.android.launcher U=0 sz=1}      ... ... ... ...      * Hist #0: ActivityRecord{429a1760 u0 com.android.launcher/com.android.launcher2.Launcher t1}          ... ... ... ...    Running activities (most recent first):      TaskRecord{43525058 #28 A=com.android.systemui U=0 sz=1}        Run #1: ActivityRecord{428d1ae8 u0 com.android.systemui/.recent.RecentsActivity t28}      TaskRecord{429a35f8 #1 A=com.android.launcher U=0 sz=1}        Run #0: ActivityRecord{429a1760 u0 com.android.launcher/com.android.launcher2.Launcher t1}    mLastPausedActivity: ActivityRecord{428d1ae8 u0 com.android.systemui/.recent.RecentsActivity t28}  Stack #1:    Task id #25    * TaskRecord{42b0ee20 #25 I=com.iderzheng/.SingleTaskActivity U=0 sz=5}      numActivities=5 rootWasReset=false userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true      intent={cmp=com.iderzheng/.SingleTaskActivity}      realActivity=com.iderzheng/.SingleTaskActivity      Activities=[ActivityRecord{42a7e160 u0 com.iderzheng/.SingleTaskActivity t25}, ActivityRecord{42bffdf0 u0 com.iderzheng/.StandardActivity t25}, ActivityRecord{42e9e8f8 u0 com.iderzheng/.SingleTopActivity t25}, ActivityRecord{434c2238 u0 com.iderzheng/.StandardActivity t25}, ActivityRecord{4279d2d8 u0 com.iderzheng/.SingleTopActivity t25}]      askedCompatMode=false      lastThumbnail=null lastDescription=null      lastActiveTime=6229735 (inactive for 357s)      * Hist #4: ActivityRecord{4279d2d8 u0 com.iderzheng/.SingleTopActivity t25}          packageName=com.iderzheng processName=com.iderzheng          launchedFromUid=10124 launchedFromPackage=com.iderzheng userId=0          app=ProcessRecord{4312cbb0 3700:com.iderzheng/u0a124}          Intent { cmp=com.iderzheng/.SingleTopActivity bnds=[328,580][656,870] }          frontOfTask=false task=TaskRecord{42b0ee20 #25 I=com.iderzheng/.SingleTaskActivity U=0 sz=5}          taskAffinity=com.iderzheng          realActivity=com.iderzheng/.SingleTopActivity          baseDir=/data/app/com.iderzheng-1.apk          dataDir=/data/data/com.iderzheng          stateNotNeeded=false componentSpecified=true mActivityType=0          compat={320dpi} labelRes=0x7f0a0013 icon=0x7f020057 theme=0x7f0b0000          config={1.0 310mcc?mnc en_US ldltr sw384dp w384dp h567dp 320dpi nrml port finger -keyb/v/h -nav/h s.7}          launchFailed=false launchCount=0 lastLaunchTime=-1h40m33s397ms          haveState=false icicle=null          state=RESUMED stopped=false delayedResume=false finishing=false          keysPaused=false inHistory=true visible=true sleeping=false idle=true          fullscreen=true noDisplay=false immersive=false launchMode=1          frozenBeforeDestroy=false thumbnailNeeded=false forceNewConfig=false          mActivityType=APPLICATION_ACTIVITY_TYPE          thumbHolder: 42b0ee20 bm=null desc=null          waitingVisible=false nowVisible=true lastVisibleTime=-5m56s862ms    ... ... ... ...    Running activities (most recent first):      TaskRecord{42b0ee20 #25 I=com.iderzheng/.SingleTaskActivity U=0 sz=5}        Run #7: ActivityRecord{4279d2d8 u0 com.iderzheng/.SingleTopActivity t25}      TaskRecord{429e9558 #24 A=com.iderzheng U=0 sz=1}        Run #6: ActivityRecord{429d5408 u0 com.iderzheng/.SingleInstanceActivity t24}      TaskRecord{42b0ee20 #25 I=com.iderzheng/.SingleTaskActivity U=0 sz=5}        Run #5: ActivityRecord{434c2238 u0 com.iderzheng/.StandardActivity t25}        Run #4: ActivityRecord{42e9e8f8 u0 com.iderzheng/.SingleTopActivity t25}        Run #3: ActivityRecord{42bffdf0 u0 com.iderzheng/.StandardActivity t25}        Run #2: ActivityRecord{42a7e160 u0 com.iderzheng/.SingleTaskActivity t25}      TaskRecord{4282e508 #23 A=com.iderzheng U=0 sz=2}        Run #1: ActivityRecord{429655d8 u0 com.iderzheng/.StandardActivity t23}        Run #0: ActivityRecord{429564e0 u0 com.iderzheng/.MainActivity t23}   ... ... ... ...  Recent tasks:  ... ... ... ...

整個log顯示了當前所有在啟動並執行任務棧,它們的id分別是什麼。對於每個Task,也有Activity數量等資訊,同時也列出了其中的Activity列表,並且對於每個Activity也有比較詳細的描述,比如啟動它的Intent的內容。

如果覺得內容過多,只想看看棧的內容,也可以直接跳到”Running activities (most recent first)”那部分,比較簡潔而又明了的列出了棧中得Activity列表,就能知道當按下返回鍵的時候會應該會回到哪個Activity以後是要退出程式。

對於”Running activitie”s的內容在dumpsys activity中就有,並不需要dumpsys activity activities,也可以用下邊的指令來限制僅輸出”Running activities”列表:

adb shell dumpsys activity activities | sed -En -e ‘/Running activities/,/Run #0/p‘
缺點

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

 

adb shell對於調試Android程式有很多的協助,可惜對於adb指令都沒有比較全面詳細而又系統的教程。只能靠在實踐中慢慢摸索,從網上零星介紹中獲得。

Download Source Code for Testing

References:
  1. Android Debug Bridge | Android Developers
  2. How to know which activities are running in Android | The skiing cube
  3. Wrightrocket: Using dumpsys commands in the Android adb shell
  4. activity – How to find back stack activities in an android application? – Stack Overflow
  5. What’s Android ADB shell ‘dumpsys’ tool and it’s benefits? – Stack Overflow

使用adb shell dumpsys檢測Android的Activity任務棧

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.