Android開發藝術(1)——Activity的生命週期和啟動模式

水瓶阿遠發表於2017-11-21

Activity生命週期的分析

從兩種情況來分析

  • 典型情況(使用者正常參與的情況)
  • 異常情況(由於記憶體不足,被系統殺掉、Configuration改變等)

典型情況下的生命週期分析

  • onPause不能太耗時,因為上一個Activity執行完之後 下個Activity的onCreate/onStart/onResume/onResume才能執行,所以不要在onPause中執行耗時才做,使新的Activity儘快切到前臺
從Activity1到Activity2
03-06 20:56:55.190 com.csy.chapter1 D/Activity1: onCreate-->
03-06 20:56:55.210 com.csy.chapter1 D/Activity1: onStart-->
03-06 20:56:55.210 com.csy.chapter1 D/Activity1: onResume-->
03-06 20:56:59.500 com.csy.chapter1 D/Activity1: onPause耗時操作開始-->
03-06 20:57:01.510 com.csy.chapter1 D/Activity1: onPause耗時操作結束-->
                                                Activity2出現
03-06 20:57:01.520 com.csy.chapter1 D/Activity2: onCreate-->
03-06 20:57:01.520 com.csy.chapter1 D/Activity2: onStart-->
03-06 20:57:01.520 com.csy.chapter1 D/Activity2: onResume-->
03-06 20:57:01.990 com.csy.chapter1 D/Activity1: onStop-->
從Activity2返回Activity1
03-06 21:01:13.490 com.csy.chapter1 D/Activity2: onPause耗時操作開始-->
03-06 21:01:15.490 com.csy.chapter1 D/Activity2: onPause耗時操作結束-->
                                                Activity1
03-06 21:01:15.490 com.csy.chapter1 D/Activity1: onRestart-->
03-06 21:01:15.490 com.csy.chapter1 D/Activity1: onStart-->
03-06 21:01:15.490 com.csy.chapter1 D/Activity1: onResume-->
03-06 21:01:15.890 com.csy.chapter1 D/Activity2: onStop-->
03-06 21:01:15.890 com.csy.chapter1 D/Activity2: onDestroy-->複製程式碼
  • onStop可以做一些稍微重量級的操作,不過也別太耗時
  • onDestory中可以做資源的釋放等操作
  • 彈出一個新的Activity,舊的會調onPause->onStop,特殊的,如果新的Activity是透明主題,那麼舊的Activity不會呼叫onStop,因為它僅僅是失去了焦點,無法互動,還是可見的、如果彈出的是Dialog棧頂Activity不會走任何回撥
  • onStart、onStop是從是否可見角度來說的,onPause和onResume是從是否位於前臺(可互動)角度來說的,除此之外沒啥區別。注意一下第一條0 0

異常情況下的生命週期

資源相關的系統配置發生改變導致Activity被殺死並重新建立

比如橫屏和豎屏會使用不同的資原始檔,如果螢幕方向發生改變,activity就會被銷燬重建,除非我們指定不要讓他銷燬重建。當然,這種銷燬重建會呼叫onSaveInstanceState來儲存activity的狀態,重建的時候會呼叫onRestoreInstanceState來恢復(onSaveInstanceState儲存的資料,在onCreate和onRestoreInstanceState中可以拿到)

onRestoreInstanceState會在onStart之前呼叫

activity被意外終止的時候,activity會呼叫onSaveInstanceState,然後委託window,接著window委託他內部的view,也就是頂層View(一般是DecorView),然後DecorView委託子View。。。一直往下到每一個View。每個View都有onSaveInstanceState和onRestoreInstanceState方法的。這種機制類似於事件分發機制

onSaveInstanceState只有在activity即將被銷燬,並且有機會重建的時候才會被呼叫。簡單理解為就是異常終止,並且會馬上重建,對比按返回鍵銷燬和旋轉螢幕銷燬就好理解了。actvity銷燬後儲存的資料在onCreate和onRestoreInstanceState中都可以拿到,但是onCreate中需要做非空判斷,onRestoreInstanceState不需要,因為onRestoreInstanceState一呼叫,一定是有資料需要恢復了

按Home鍵或者啟動新Activity仍然會單獨觸發onSaveInstanceState的呼叫。因為Home之後或者新的activity出現之後,舊的activity到了後臺,都有很大可能被殺(像小米、華為等不到10秒肯定被殺死)。

資源記憶體不足導致低優先順序Activity被殺

activity的優先順序

  • 前臺Activity
  • 可見但非前臺,比如彈出dialog,我的理解是onPause之後的(不一定準確0 0)
  • 後臺Activity
    如果一個程式中沒有四大元件在執行,很快會被殺,所以後臺任務一般要在service中執行。我的理解是,如果在activity中開一個執行緒去做後臺任務,activity不在前臺的時候就容易被殺,比如回到桌面

如果不想在螢幕旋轉後重建activity可以在清單檔案中指定configChanges屬性,orientation、keyboardHidden,API13之後還需要指定screenSize。所有的Config(具體查表)改變都會導致Activity重建,這裡配置之後,被配置的選項改變不會使activity重建,但會回撥onConfigurationChange方法,通知我們改變了,然後根據自己的需求做一些事情就好了。

Activity的啟動模式

當一個任務棧中沒有activity的時候,這個棧就會被回收

  • standard (預設模式)
  • singleTop

    棧頂複用,當被複用的時候,會呼叫onNewIntent,引數中可以取到啟動新activity攜帶的intent,並且onCreate、onStart不會被呼叫

  • singleTask(通常將MainActivity設定成次模式)

    棧內複用,與singletop類似,如果棧記憶體在就複用至於棧頂,並將該Activty以上的Activity都銷燬。這裡指的是同一個App中啟動這個singleTask的Activity,如果是其他程式以singleTask來啟動這個Activity,它會建立一個新的任務棧。有一點需要注意,如果這個Activity後臺已經在一個任務棧中了,那麼啟動後這個任務棧將一起被切換到前臺。將MainActivity設為singleTask模式,然後在將要退出的Activity跳轉到MainActivity,從而將MainActivity之上的Activity都清除,然後重新Activity的OnNewIntent()方法,在方法中加上finish()即可退出整個應用。

  • singleInstance

adb shell dumpsys activity 檢視棧資訊複製程式碼

啟動一個activity的時候,一般新的activity會在呼叫startactivity的context所在的任務棧中,但是applicationcontext不存在於任務棧,所以需要為新的activity指定FLAG_ACTIVITY_NEW_TASK標記,不明白為什麼在清單檔案中指定不行

TaskAffinity(任務相關性)可以指定一個activity所需的棧名(預設為他的包名),他只能和singleTask和allowTaskRepareniting配合使用,其他情況無效

如果沒有指定TaskAffinity,新啟動的activity會繼承啟動他activity的棧(singleInstance會進入新的棧,但是實驗發現,棧名是一樣的,不過不是一個物件,相當於建立一個新的棧名相同的棧)

allowTaskReparenting可以指明一個activity

onNewIntent被呼叫的那些情況下,onCreate、onStart不會被呼叫

Activity的flags

摘自Android開發藝術圖書勘誤:第1.2.2小節中,Activity的Flags,這一節的內容直接翻譯了Android官方文件(developer.android.com/guide/compo…
但是經過例項驗證,發現書中的描述不準確.(或者說官方文件中的描述不準確),結論為:Flags並不能簡單地等同於啟動模式,這一塊內容需要進一步驗證。

IntentFilter的匹配規則

這個直接結合生活就很好理解了

action/data在intent中是set,category是add,所以就知道前兩個是唯一的,category是可以配多個的

  • action行為、動作

    • 在清單檔案中配置的時候,action表示“我可以幹什麼”,所以可以配置多個:我可以展示圖片、我可以展示音訊
    • 在程式碼中指定意圖的時候只能指定一個action:我將要幹什麼,比如我要展示圖片,或者我要播放音訊,則分別跳轉到相應的activity,不能我又要播放音訊又要展示圖片
    • 所以action就是清單檔案中配置多個,指定意圖的時候只能指定一個
  • category分類

    • 在清單檔案中配置的時候,配置我屬於什麼什麼,比如:我屬於預設分類、運動類、戶外類(注意,這裡最好指明自己屬於預設類,這樣在查詢的時候,別人都能找到)
    • 在程式碼中指定意圖的時候可以指定我要:運動類並且是戶外類,或者輪滑類(注意,系統會自動加上預設類)
    • data資料形式這個跟action類似,他是根據資料的規則來匹配的,action是我要找到"可以幹什麼"的activity,data是我要找到"可以接收data"的activity,data主要分為URI和mimeType兩部分,一個代表資源位置,一個代表資源型別
    • 如果要為Intent指定完整的data,必須呼叫setDataAndType方法,不能先呼叫setData在呼叫setType,因為這個兩個方法彼此會清除對方的值,原始碼中可以看出。
  • 常見的category

CATEGORY_ALTERNATIVE  
設定這個activity是否可以被認為是使用者正在瀏覽的資料的一個可選擇的action  

CATEGORY_APP_BROWSER  
和ACTION_MAIN一起使用,用來啟動瀏覽器應用程式  

CATEGORY_APP_CALCULATOR  
和ACTION_MAIN一起使用,用來啟動計算器應用程式  

CATEGORY_APP_CALENDAR  
和ACTION_MAIN一起使用,用來啟動日曆應用程式  

CATEGORY_APP_CONTACTS  
和ACTION_MAIN一起使用,用來啟動聯絡人應用程式  

CATEGORY_APP_EMAIL  
和ACTION_MAIN一起使用,用來啟動郵件應用程式  

CATEGORY_APP_GALLERY  
和ACTION_MAIN一起使用,用來啟動相簿應用程式  

CATEGORY_APP_MAPS  
和ACTION_MAIN一起使用,用來啟動地圖應用程式  

CATEGORY_APP_MARKET  
這個activity允許使用者瀏覽和下載新的應用程式  

CATEGORY_APP_MESSAGING  
和ACTION_MAIN一起使用,用來啟動簡訊應用程式  

CATEGORY_APP_MUSIC  
和ACTION_MAIN一起使用,用來啟動音樂應用程式  

CATEGORY_BROWSABLE   
能夠被瀏覽器安全呼叫的activity必須支援這個category  

CATEGORY_DEFAULT   
設定這個activity對於預設的action是否是一個可選的  

CATEGORY_EMBED   
可以執行在父activity容器內  

CATEGORY_HOME   
主activity,當應用程式啟動時,它是第一個顯示的activity  

CATEGORY_LAUNCHER  
應該在上層的啟動列表裡顯示  

CATEGORY_MONKEY  
這個activity可能被monkey或者其他的自動測試工具執行  

CATEGORY_OPENABLE   
用來指示一個GET_CONTENT意圖只希望ContentResolver.openInputStream能夠開啟URI  

CATEGORY_PREFERENCE   
這個activity是一個選項卡  

CATEGORY_SAMPLE_CODE   
作為一個簡單的程式碼示例使用(一般情況下不使用)  

CATEGORY_SELECTED_ALTERNATIVE  
設定這個activity是否可以被認為是使用者當前選擇的資料的一個可選擇的action  

CATEGORY_TAB   
想要在已有的TabActivity內部作為一個Tab使用  

CATEGORY_TEST  
供測試使用(一般情況不使用)  

CATEGORY_UNIT_TEST  
聯合測試使用複製程式碼

對於Service和BroadcastReceiver,這套匹配規則也適用,不過對於Service,官方推薦使用顯示意圖。在使用隱式意圖的時候,最好先做個判斷,判斷是否有可以匹配的activity

通過resolveActivity()方法可以判斷是否有Activity能夠匹配隱式Intent

相關文章