標籤:
1.Activity簡介
Activity是Android一個非常重要的使用者介面(四大組件之一),是可見的,主要是使用者和應用程式之間進行互動的介面。在每個Activity中都可以放很多控制項,所以也可以把Activity看作控制項的容器。
2.Activity生命週期
- onCreate:主要執行初始化工作,比如用setContentView載入布局介面
- onStart:Activity由不可見變為可見,但是不在前台,不可以與使用者進行互動
- onResume:使Activity位於返回棧的頂端,並出現在前台,可與使用者進行互動
- onRestart:Activity的重啟,由不可見變為可見
- onPause:表明Activity正在停止,正常情況下onStop會緊接著調用。但是也有特殊情況,若這個時候需要快速的揮刀當前Activity,那麼onResume也會被調用。這個方法的執行一定要快,不然會影響棧頂的Activity的使用。
- onStop:Activity由部分不可見變為完全不可見,可做一些重量級的回收工作,同樣不能很耗時。
- onDestory:銷毀Activity並釋放資源。
其中onCreate和onDestory為完整的生命週期,onStart和onStop為可見生命週期,onResume和onPause為前台生命週期。
注意點
- 當使用者開啟新的Activity或切換回到案頭的時候,回調為onPause->onStop,但是若Activity採用的為透明主題,則不會回調onStop。
- 一般情況下,Activity有不可見變為可見,onRestart才會調用。
- onStart和onStop控制Activity在可見和不可見的狀態之間轉換,onResume和onPause控制Activity在前台或非前台之間轉換。
- 當由Activity A ->Activity B時,回調的順序為onPause(A)->onCreate(B)->onStart(B)->onStop(A),因此不可以在onPause上做重量級操作。
- 如果應用長時間處於stopped狀態並且此時系統記憶體極為緊張的時候,系統就會回收Activity,此時系統在回收之前會回調onSaveInstanceState方法來儲存應用的資料Bundle。當該Activity重新建立的時候,儲存的Bundle資料就會傳遞到onRestoreSaveInstanceState方法和onCreate方法中,這就是onCreate方法中Bundle savedInstanceState參數的來源(onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做資料還原)。
onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被調用的。
onSaveInstanceState的調用遵循一個重要原則,即當系統“未經你許可”時銷毀了你的activity,則onSaveInstanceState會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你儲存你的資料。
onRestoreInstanceState被調用的前提是,activity“確實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示activity的時候,使用者按下HOME鍵回到主介面,然後使用者緊接著又返回到activity,這種情況下activity一般不會因為記憶體的原因被系統銷毀,故activity的onRestoreInstanceState方法不會被執行。
3.異常生命週期
1.資源相關的系統配置發生改變導致Activity被殺死並重建
資源相關的系統配置發生改變,舉個栗子。當前Activity處於豎屏狀態的時候突然轉成橫屏,系統配置發生了改變,Activity就會銷毀並且重建,其onPause, onStop, onDestory均會被調用。因為實在異常情況下終止的,所以系統會調用onSaveInstanceState來儲存當前Activity狀態。這個方法是在onStop之前,與onPause沒有固定的時序關係。當Activity重建的時候系統會把onSaveInstanceState所儲存的Bundle作為對象傳遞給onRestoreInstanceState和onCreate方法。
註:
- View的源碼中每個View都有onSaveInstanceState和onRestoreInstanceState這兩個方法。
- 接收位置可以是onRestoreInstanceState和onCreate方法,區別是:onRestoreInstanceState如果被調用,參數Bundle一定是有值的,在onCreate中需要判斷參數是否為null。
- onSaveInstanceState只有在Activity即將銷毀並有機會重新顯示時才會調用,正常銷毀的Activity生命週期中不會調用,比如:旋轉螢幕,按Home鍵,啟動新Activity等。
2.資源記憶體不足導致低優先順序的Acitvity被殺死
Activity優先順序
- 前台Activity——正在和使用者互動的Activity,優先順序最高
- 可見但非前台Activity——Activity中彈出的對話方塊導致Activity可見但無法互動
- 後台Activity——已經被暫停Activity,優先順序最低
系統記憶體不足是,會按照以上順序殺死Activity,並通過onSaveInstanceState和onRestoreInstanceState這兩個方法來儲存和恢複資料。
不讓Activity重新建立的方法
系統配置有很多內容,當某項改變時,我們不想讓Activity重新建立可以在AndroidMainfest中給Activity指定configChanges屬性。比如
android:configChanges="orientation"1
configChanges屬性非常多,具體可參考官方文檔
常用的有locale, orientation和keyboardHidden這三個。
4.Activity任務棧和四種啟動模式
應用內的Activity是被任務棧Task來管理的,一個Task中的Activity可以來自不同的應用,同一個應用的Activity也可能不在同一個Task中。預設情況下,任務棧依據棧的後進先出原則管理Activity,但是Activity可以設定一些“特權”打破預設的規則,主要是通過在AndroidManifest檔案中的屬性android:launchMode或者通過Intent的flag來設定。
standard:預設的啟動模式,該模式下會產生一個新的Activity,同時將該Activity執行個體壓入到棧中(不管該Activity是否已經存在在Task棧中,都是採用new操作)。例如: 棧中順序是A B C D ,此時D通過Intent跳轉到A,那麼棧中結構就變成 A B C D A,點擊返回按鈕的 顯示順序是 D C B A,依次摧毀。
singleTop:在singleTop模式下,如果當前Activity D位於棧頂,此時通過Intent跳轉到它本身的Activity(即D),那麼不會重新建立一個新的D執行個體(走onNewIntent()),所以棧中的結構依舊為A B C D,如果跳轉到B,那麼由於B不處於棧頂,所以會建立一個B執行個體並壓入到棧中,結構就變成了A B C D B。應用執行個體:三條推送,點進去都是一個activity。
singleTask:在singleTask模式下,Task棧中只能有一個對應Activity的執行個體。例如:現在棧的結構為A B C D,此時D通過Intent跳轉到B(走onNewIntent()),則棧的結構變成了:A B。其中的C和D被棧彈出銷毀了,也就是說位於B之上的執行個體都被銷毀了。如果系統已經存在一個執行個體,系統就會將請求發送到這個執行個體上,但這個時候,系統就不會再調用通常情況下我們處理請求資料的onCreate方法,而是調用onNewIntent方法。通常應用於首頁,首頁肯定得在棧底部,也只能在棧底部。
singleInstance:singleInstance模式下會將開啟的Activity壓入一個建立的任務棧中。例如:Task棧1中結構為:A B C,C通過Intent跳轉到了D(D的啟動模式為singleInstance),那麼則會建立一個Task 棧2,棧1中結構依舊為A B C,棧2中結構為D,此時螢幕中顯示D,之後D通過Intent跳轉到D,棧2中不會壓入新的D,所以2個棧中的情況沒發生改變。如果D跳轉到了C,那麼就會根據C對應的啟動模式在棧1中進行對應的操作,C如果為standard,那麼D跳轉到C,棧1的結構為A B C C,此時點擊返回按鈕,還是在C,棧1的結構變為A B C,而不會回到D。
Intent Flag啟動模式
(1)Intent.FLAG_ACTIVITY_NEW_TASK:使用一個新的task來啟動Activity,一般用在service中啟動Activity的情境,因為service中並不存在Activity棧。
(2)Intent.FLAG_ACTIVITY_SINGLE_TOP:類似andoid:launchMode=”singleTop”
(3)Intent.FLAG_ACTIVITY_CLEAR_TOP:類似andoid:launchMode=”singleTask”
(4)Intent.FLAG_ACTIVITY_NO_HISTORY:使用這種模式啟動Activity,當該Activity啟動其他Activity後,該Activity就消失了,不會保留在task棧中。例如A B,在B中以這種模式啟動C,C再啟動D,則當前的task棧變成A B D。
清空任務棧
(1)clearTaskOnLaunch:每次返回該Activity時,都將該Activity之上的所有Activity都清除。通過這個屬性可以讓task每次在初始化的時候都只有這一個Activity。
(2)finishOnTaskLaunch:clearTaskOnLaunch作用在別的Activity身上,而finishOnTaskLaunch作用在自己身上。通過這個屬性,當離開這個Activity所在的task,那麼當使用者再返回時,該Activity就會被finish掉。 [暫時還不明白這個有什麼作用]
(3)alwaysRetainTaskState:如果將Activity的這個屬性設定為true,那麼該Activity所在的task將不接受任何清理命令,一直保持當前task狀態,相當於給了task一道”免死金牌”。
5.IntentFilter的匹配規則
(1)IntentFilter中的過濾資訊有action、category、data,為了匹配過濾列表,需要同時匹配過濾列表中的action、category、data資訊,否則匹配失敗。一個過濾列表中的action、category、data可以有多個,所有的action、category、data分別構成不同類別,同一類別的資訊共同約束當前類別的匹配過程。只有一個Intent同時匹配action類別、category類別和data類別才算完全符合,只有完全符合才能成功啟動目標Activity。此外,一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intenf-filter即可成功啟動對應的Activity。
<category android:name="com.ryg.category.c" /><category android:name="com.ryg.category.d" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="text/plain" />
(2)action匹配規則
只要Intent中的action能夠和過濾規則中的任何一個action相同即可匹配成功,action匹配區分大小寫。
Intent中如果有category那麼所有的category都必須和過濾規則中的其中一個category相同,如果沒有category的話那麼就是預設的category,即android.intent.category.DEFAULT,所以為了Activity能夠接收隱式調用,配置多個category的時候必須加上預設的category。
data的結構很複雜,文法大致如下:
<data android:scheme="string"android:host="string"android:port="string"android:path="string"android:pathPattern="string"android:pathPrefix="string"android:mimeType="string" />
主要由mimeType和URI組成,其中mimeType代表媒體類型,而URI的結構也複雜,大致如下:
://:/[]|[]|[pathPattern]
例如content://com.example.project:200/folder/subfolder/etc
scheme、host、port分別表示URI的模式、主機名稱和連接埠號碼,其中如果scheme或者host未指定那麼URI就無效。
path、pathPattern、pathPrefix都是表示路徑資訊,其中path表示完整的路徑資訊,pathPrefix表示路徑的首碼資訊;pathPattern表示完整的路徑,但是它裡麵包含了萬用字元(*)。
data匹配規則:Intent中必須含有data資料,並且data資料能夠完全符合過濾規則中的某一個data。
URI有預設的scheme!
如果過濾規則中的mimeType指定為image/或者text/等這種類型的話,那麼即使過濾規則中沒有指定URI,URI有預設的scheme是content和file!如果過濾規則中指定了scheme的話那就不是預設的scheme了。
//URI有預設值<intent-filter> <data android:mimeType="image/*"/> ...</intent-filter>//URI預設值被覆蓋<intent-filter> <data android:mimeType="image/*" android:scheme="http" .../> ...</intent-filter>
如果要為Intent指定完整的data,必須要調用setDataAndType方法!
不能先調用setData然後調用setType,因為這兩個方法會彼此清除對方的值。
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
data的下面兩種寫法作用是一樣的:
<intent-filter> <data android:scheme="file" android:host="www.github.com"/></intent-filter><intent-filter> <data android:scheme="file"/> <data android:host="www.github.com"/></intent-filter>
如何判斷是否有Activity能夠匹配我們的隱式Intent?
(1)PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就會返回null
(2)PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity資訊
針對Service和BroadcastReceiver等組件,PackageManager同樣提供了類似的方法去擷取成功匹配的組件資訊,例如queryIntentServices、queryBroadcastReceivers等方法
有一類action和category比較重要,它們在一起用來標明這是一個入口Activity,並且會出現在系統的應用列表中。
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /></intent-filter>
6.Activity的構成
Activity的構成不是一個Activity對象加上一個布局檔案那麼簡單,在Activity和開發人員中間還隔著兩層。實際上視圖會被設定給一個Window類,Window類中有一個DecorView,這就是整個視窗的頂級視圖。開發人員設定的布局會被設定到這個DecorView的mContentParent布局中。
這樣子,使用者介面就被添加到了系統布局中,而系統布局會為我們設定好標題列地區等。
Activity->PhoneWindow->DecorView->DefaultLayout->ViewGroup:mContentParent->使用者自己的xml布局
Android之Activity總結