談起Android程序開發,就須要瞭解其四個主要的部件:Activity、Service、ContentProvider、 BroadcastReceiver。而其中Activity是惟一直接控制程序界面呈現,直面用戶操做的部件(固然BrowadCastReceiver也能經過桌面控件(App Widgets)來呈現有限的操做界面)。Android對於Activity有嚴格的生命週期控制,以限制開發者在適當的回調函數裏的放上合適的代碼。對於多個Activity的轉換,Android也有很是好的管理和流暢的切換,對此Android還引入了任務棧(Task Stack)的概念,這個概念對於Android設備上得返回按鍵有極其重要的聯繫。html
(大部分文檔都將其表述爲Tasks and Back Stack,但從官方文檔的描述來看,Android的相對於Activity講到的Task都視爲一個存放Activities的Stack,因此將其稱爲Task Stack也不爲過。)java
在AndroidManifest中申明所要用到的Activity時能夠設置不一樣的launchMode
來獲得不一樣的Activity「啓動」效果。在使用startActivity
開啓新的Activity時,傳入的Intent也能夠設置不一樣的Flag來達到不一樣的效果。另外一方面,在Activity啓動時它可能又開啓了另外一個Activity,或者調用了finish()
函數終結了Activity。android
這使得Activity棧變得沒法掌握,有時候按下返回按鈕或者點擊關閉當前Activity的操做,都不知道Android系統會把程序帶到那個Activity,不肯定這是不是最後一個Activity以至退出了整個程序。亦或者一些按鈕和操做循環產生Activity而形成內存膨脹。對於這些問題,若是可以在調試期間知道當前任務棧的狀況,就能很方便的觀察和發現問題存在的緣由,進而選擇正確的launchMode
,設置恰當Intent
的Flag
來使程序達到預期的效果。shell
Android提供了ActivityManger來幫助開發者瞭解運行期間的狀態,經過調用getRunningTasks(int)
方法,就能夠在獲得RunningTaskInfo
的列表,其表明着當前Android設備正在運行着的Task。從RunningTaskInfo中又能夠進一步獲得更多的信息。bash
例如文中提供的示例程序中定義了4個具備不一樣launchMode
的Activity,每點擊一次菜單欄上得選項就會彈出一個新的Activity(或者將指定Singleton的Activity置前)。
Activity上顯示的數字則指示startActivity()
被第幾回調用時開啓了這個Activity。有一些Singleton的會顯示多個數字,也代表它是被複用的。
由於在onCreate()
方法上放置了上述代碼,因此觀察log就能發現當前有多少個Task在被執行,每一個Task又有多少個Activities。app
D/IDER ( 3700): ===================== D/IDER ( 3700): --------------------- D/IDER ( 3700): id: 25 D/IDER ( 3700): description: null D/IDER ( 3700): number of activities: 4 D/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: 24 D/IDER ( 3700): description: null D/IDER ( 3700): number of activities: 1 D/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: 23 D/IDER ( 3700): description: null D/IDER ( 3700): number of activities: 2 D/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: 1 D/IDER ( 3700): description: null D/IDER ( 3700): number of activities: 1 D/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對象,所以除了類的名字沒有其餘更多信息。ide
Activity的建立和銷燬都會有相應的回調函數:onCreate()
,onDestroy()
。所以能夠自建一個靜態全局Stack
對象,在onCreate()
時候講當前Activity對象加入到Stack
中,而在onDestroy()
時把它從Stack中移除。這樣咱們就隨時能夠知道當前Activity的詳細狀況了。wordpress
要讓全部Activity的onCreate()
和onDestroy()
方法上有對應的進出棧的方法,要麼有統一的基類,要麼強制每一個Activity都加入這些代碼,但兩種方式都不完美。另外也很難模擬singleTask
這類會建立出新的Task的狀況,這時光使用一個Stack
就不足夠了,要考慮全部的狀況又不太可能。再者如同使用ActivityManager同樣這些代碼也應該只出如今調試階段函數
Android還爲開發者提供了adb(Android Debug Bridge),這是很是強大的調試工具。最經常使用的天然是logcat來顯示日誌記錄。另一個很強大的指令就是這裏要提到的dumpsys
。dumpsys
還能夠添加不一樣的參數來指示須要輸出哪一類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指令都沒有比較全面詳細而又系統的教程。只能靠在實踐中慢慢摸索,從網上零星介紹中得到。