Activity
一、四種形態(tài)
運行狀態(tài): 當 Activity 處于棧的頂層,可見,并可與用戶進行交互 onResume()--> onPause()
暫停狀態(tài): 當 Activity 被非全屏的或者透明的 Activity 遮擋后,原來的 Activity 處于暫停狀態(tài) onPause() 被調(diào)用 ,系統(tǒng)內(nèi)存極低時會被回收
停止狀態(tài): 當 Activity 被置于后臺(例如被其它Activity完全遮擋,或者用戶按下 HOME 鍵回到桌面) onStop() 被調(diào)用,系統(tǒng)內(nèi)存極低時會被回收
終止狀態(tài): 當 Activity 從未被創(chuàng)建過或者 Activity 被銷毀(調(diào)用了 Activity 的 finish() 方法或者用戶按下 BACK 鍵,表現(xiàn)為 onDestory() 被調(diào)用,但不一定釋放了內(nèi)存)
二、生命周期
正常聲明周期
onCreate() 表示 Activity 正在被創(chuàng)建,執(zhí)行加載布局資源、初始化數(shù)據(jù)等
onRestart() 當 Activity 從不可見重新變見變?yōu)榭梢姇r調(diào)用
onStart() 當 Activity 正在被啟動,Activity 已經(jīng)可見,但是還沒用開始活動,還無法和用戶進行交互,Activity 的布局界面還不可見
onResume() 當 Activity 可見,并且已經(jīng)開始活動,可與用戶進行交互,并且在這個方法中將布局顯示到屏幕上
onPause() 當 Activity 失去焦點,不可與用戶交互,暫停,可以做一些資源回收,停止動畫操作,不能耗時
onStop() 當 Activity 完全進入后臺,可以做一些稍微重量級的回收工作,不能耗時
onDestory() 表示 Activity 即將被銷毀,可以做一些回收工作和最終的資源釋放
生命周期的注意問題
當Activity被激活時: onCreate() -> onStart() -> onResume()
當 Activity 被銷毀時:onPause() -> onStop() -> onDestory()
當 Activity 從不可見重新變見變?yōu)榭梢姇r,會經(jīng)歷:onStop() -> onRestart() -> onStart() -> onResume()
當新的 Activity 被激活時:原來的 Activity 先 onPause(),當新的 Activity 已經(jīng)完全激活(顯示在前臺)時,原來的 Activity 會 onStop()
onPause() --> onCreate() --> onStart() --> onResume() --> onStop()當 A_Activity 被 B_Activity 不完全遮擋,B_Activity 為透明或非全屏,B_Activity 被銷毀時:A_Activity 被重新顯示,會經(jīng)歷:onPause() -> onResume()
非透明 Activity 啟動透明或非全屏 Activity ,第一個 Activity 不會執(zhí)行 onStop ,因為一直可見
onPause() 方法是系統(tǒng)能銷毀當前 Activity 之前調(diào)用的最后一個方法,所以一些數(shù)據(jù)的保存應(yīng)該放在onPause() 方法中,但是 onPause() 方法中不能有太多操作,因為onPause() 中任何一個阻塞的過程都會影響向下一個Activity 轉(zhuǎn)換
Activity 的 onDestory 調(diào)用之后,其中如果有子線程或異步任務(wù)在運行,Thread 和 AsyncTask 并不會停止,會引起 Activity 內(nèi)存泄漏,必須在 onDestory 方法中處理這些線程和 AsyncTask
在 AsyncTask 或者 handler 的返回方法中,必須對需要處理的 UI 使用弱引用,這樣不但可以保證 Activity 中的 UI 控件被正?;厥?,并且在處理 UI 控件時做判空處理,還能保證在 UI 不存在時不會拋出異常
調(diào)用 finish 方法時的聲明周期 onPause --> onStop --> onDestory
非正常生命周期
第一種非正常生命周期發(fā)生在當資源相關(guān)的系統(tǒng)配置發(fā)生改變時,例如橫豎屏切換時加載的資源會發(fā)生不同,導(dǎo)致 Activity 被殺死并重建
第二種非正常生命周期發(fā)生情況是在系統(tǒng)內(nèi)存不足時導(dǎo)致低優(yōu)先級的 Activity 被殺死,并在重啟 Activity 時重建
非正常生命周期中 onSaveInstanceState() 方法中保存狀態(tài)數(shù)據(jù),onCreate() 和 onRestoreInstanceState() 方法中都可以獲取保存的數(shù)據(jù)并恢復(fù)狀態(tài)
非正常生命周期注意問題
只有Activity 異常退出以及重建時以上兩個方法才會被調(diào)用
系統(tǒng)銷毀Activity時會調(diào)用保存數(shù)據(jù)的方法 onSaveInstanceState()
onRestoreInstanceState() 方法調(diào)用在 onStart() 之后 onResume() 之前
onStart() --> onRestoreInstanceState() --> onResume()
onSaveInstanceState 調(diào)用在 onStop 之前,與onPause 沒有前后關(guān)系
系統(tǒng)在 onSaveInstanceState 方法中會調(diào)用有 id 的控件的對應(yīng)方法保存狀態(tài),如果控件沒有 id 則不會其保存
配置 configChanges 屬性后,Activity 在相應(yīng)的資源配置變化時不會調(diào)用上述兩個方法,會調(diào)用,onConfigurationChanged 方法
Activity 處于前臺或者可見時如果系統(tǒng)資源不足,也會在回收 Activity 之前調(diào)用 onSaveInstanceState 方法保存數(shù)據(jù)
onCreate 中使用保存的數(shù)據(jù),需要對 Bundle 做判空驗證,onRestoreInstanceState 方法如果被回調(diào),則 Bundle 一定不為空,建議在 onRestoreInstanceState 方法中使用保存的數(shù)據(jù)
三、啟動模式
standard 標準模式
被本應(yīng)用或非本應(yīng)用非 SingleInstance 的 Activity 啟動時,每次都會在啟動他們的那 Activity 所在的棧中創(chuàng)建一個新的 Activity,多次創(chuàng)建多次入棧
被本應(yīng)用 singleInstance 的 Activity 啟動時,如果存在以包名為名的棧,則將目標 Activity 入棧,如果不存在則創(chuàng)建以包名為棧名的棧,然后入棧
被非本應(yīng)用 singleInstance 的 Activity 啟動時,如果被啟動的 Activity 所屬應(yīng)用未啟動;將新創(chuàng)建的 Activity 放入棧名為包名新的棧中,并且只會啟動一次,之后重復(fù)啟動都不再重復(fù)創(chuàng)建新的 Activity,并且不執(zhí)行 newIntent 方法
被非本應(yīng)用 singleInstance 的 Activity 啟動時,如果被啟動的 Activity 所屬應(yīng)用已啟動并且該應(yīng)用當前棧頂 Activity 為 singleInstance,并且不存在棧名為包名的棧,則會創(chuàng)建棧名為包名的棧,然后將新啟動 Activity 入該棧,之后重復(fù)啟動都不重復(fù)創(chuàng)建新的 Activity,并且不執(zhí)行 newIntent 方法
被非本應(yīng)用 singleInstance 的 Activity 啟動時,如果被啟動的 Activity 所屬應(yīng)用已啟動并且該應(yīng)用當前棧頂 Activity 為 singleInstance,且存在棧名為包名的棧,會直接將新啟動 Activity 入該棧,多次啟動,多次創(chuàng)建并入該棧。
被非本應(yīng)用 singleInstance 的 Activity 啟動時,如果被啟動的 Activity 所屬應(yīng)用已啟動并且該應(yīng)用當前棧頂 Activity 為非 singleInstance,不論當前棧頂 Activity 所在棧棧名是否為包名也不論是否有棧名為包名的棧,都會直接將新啟動 Activity 入該棧,多次啟動,多次創(chuàng)建并入該棧。
singleTop 棧頂復(fù)用模式
singleTop 啟動時需要入的棧同 standard
啟動時,如果要加入的棧棧頂為需要啟動的 Activity,此時不會重復(fù)啟動,且會回調(diào) onNewIntent 方法
啟動時,如果要加入的棧棧頂不是需要啟動的 Activity,此時會重復(fù)啟動,且入棧
singleTask 棧內(nèi)復(fù)用模式
singleTask 啟動時需要入的??梢栽谧詴r聲明 TaskAffinity 屬性,如果沒有聲明該屬性,則需要入的棧同 standard ,不會創(chuàng)建新的任務(wù)和任務(wù)棧
singleTask 注冊時如果聲明 TaskAffinity 屬性,如果啟動時該棧存在則直接入棧,不論要啟動該 Activity 的應(yīng)用是否是同一應(yīng)用,并且模式為棧內(nèi)復(fù)用,如果該棧不存在則創(chuàng)建新任務(wù)以及棧名為 TaskAffinity 參數(shù)的棧,并入棧,再啟動是不會創(chuàng)建,onNewIntent 方法會被回調(diào)
taskAffinity 值為一個字符串,其中必須包含包名分隔符 .
系統(tǒng)創(chuàng)建新任務(wù)并實例化位于新任務(wù)底部的 Activity。但是,如果該 Activity 的一個實例已存在于一個單獨的任務(wù)中,則系統(tǒng)會通過調(diào)用現(xiàn)有實例的 onNewIntent() 方法向其傳送 Intent,而不是創(chuàng)建新實例。一次只能存在 Activity 的一個實例。
注:盡管 Activity 在新任務(wù)中啟動,但是用戶按“返回”按鈕仍會返回到前一個 Activity,不論這個前 Activity 是不是本應(yīng)用的,可以是桌面,其他應(yīng)用的界面等。如果切換其他任務(wù)棧到前臺,則啟動一個在目標棧的 Activity 即可將目標棧切換到前臺
這里有一個問題,就是如果普通棧和 singleTask 棧都存在,并且這時 singleTask 為后臺棧,普通棧為前臺,普通棧中的 Activity 都出棧之后,singleTask 的棧也不會顯示到前臺,要想將 singleTask 的棧切換到前臺,只有再次調(diào)用 singleTask 棧中的 Activity 啟動方法,此時 singleTask 棧中存在的 Activity 不會重新創(chuàng)建,會調(diào)用 onNewInent ,不過也會同時切換到前臺
TaskAffinity 只有和 singleTask 模式結(jié)合使用時才有意義,其他啟動模式時沒意義
singleInstance 單實例模式
singleInstance 獨自運行在一個任務(wù)棧中,不同應(yīng)用啟動統(tǒng)一個 singleInstance 模式的 Activity 時,該 Activity 只會創(chuàng)建一次,之后會調(diào)用 onNewIntent 方法
singleInstance 的 Activity 啟動任何 Activity (除非自己) 都會將目標 Activity 放入其他的棧中
與 "singleTask" 相同,只是系統(tǒng)不會將任何其他 Activity 啟動到包含實例的任務(wù)中。該 Activity 始終是其任務(wù)唯一僅有的成員;由此 Activity 啟動的任何 Activity 均在其他的任務(wù)中打開。
注意:如果 singleInstance 的棧在后臺,顯示到前臺的方法同 singleTask,以及切換其他棧到前臺的方式也與 singleTask 的相同
allowTaskReparenting 屬性
在這種情況下,Activity 可以從其啟動的任務(wù)移動到與其具有關(guān)聯(lián)的任務(wù)(如果該任務(wù)出現(xiàn)在前臺)
如果注冊清單中為 Activity 配置 allTaskReparenting 屬性為 true 之后,默認棧名為包名,可以通過 taskAffinity 來指定想要的棧,如果從其他任務(wù)棧啟動該 Activity 時該 Activity 指定的棧不存在,此時不會創(chuàng)建新棧,會將目標 Activity 放入啟動它的 Actiivty 所在棧,當目標 Activity 指定棧被創(chuàng)建并為前臺棧時,例如啟動該 Activity 所在的應(yīng)用,此時已經(jīng)啟動的那個 Activity 會馬上從啟動它的應(yīng)用的棧中跳回到本身指定的任務(wù)棧中,并獲取棧頂位置,此時按返回鍵效果為顯示該指定棧中的 Activity
如果從其他應(yīng)用啟動該 Activity 時該 Activity 指定棧已經(jīng)存在,這時候從其他棧啟動該 Activity 不會馬上加入指定的棧中,同樣需要在指定棧切換到前臺時,例如屏幕首頁點一下啟動該應(yīng)用的圖標,這時候該 Activity 就會自動從啟動它的應(yīng)用的棧跳回本身所指定的棧中
啟動模式注意問題
使用 singleTop/singleTask/singleInstance 時,如果當前界面是這個 Activity,那么再次啟動時 onPause --> onNewIntent --> onResume 方法會依次回調(diào),onNewIntent 中可以收到新的 Intent 數(shù)據(jù),不過getIntent 得到的還是第一次啟動時的 Intent
使用 singleTask/singleInstance 時,如果當前界面不是這個 Activity,那么再次啟動時 onNewIntent --> onRestart --> onStart --> onResume 方法會依次回調(diào),onNewIntent 中可以收到新的 Intent 數(shù)據(jù),不過getIntent 得到的還是第一次啟動時的 Intent
當注冊時指定啟動模式且通過 Intent 指定的啟動模式同時存在,則以 Intent 中指定的為主
注冊時指定啟動模式不能實現(xiàn) FLAG_ACTIVITY_CLEAR_TOP 效果
Intent 指定啟動模式不能實現(xiàn) singleInstance 效果
某個任務(wù)棧中的 Activity 切換到前臺,該任務(wù)棧也會切換到前臺
如果某應(yīng)用中啟動了別的任務(wù),在桌面點擊該應(yīng)用圖標時,該應(yīng)用自己的任務(wù)棧會回到前臺
使用 singleTask 以及 singleInstance 時一定要做好測試,因為不同的情況會由不同的表現(xiàn)
通過設(shè)置 Intent 的 Flag 來設(shè)置啟動模式
FLAG_ACTIVITY_SINGLE_TOP 與 singleTop 效果相同
FLAG_ACTIVITY_CLEAR_TOP 如果正在啟動的 Activity 已在當前任務(wù)中運行,則會銷毀當前任務(wù)頂部的所有 Activity (僅限于其啟動模式不是 standart 的情況下),并通過 onNewIntent() 將此 Intent 傳遞給 Activity 已恢復(fù)的實例(現(xiàn)在位于頂部),而不是啟動該 Activity 的新實例。
FLAG_ACTIVITY_CLEAR_TOP 通常與 FLAG_ACTIVITY_NEW_TASK 結(jié)合使用。一起使用時,通過這些標志,可以找到其他任務(wù)中的現(xiàn)有 Activity,并將其放入可從中響應(yīng) Intent 的位置。
注:如果指定 Activity 的啟動模式為 "standard",則該 Activity 也會從堆棧中移除,并在其位置啟動一個新實例,以便處理傳入的 Intent。 這是因為當啟動模式為 "standard" 時,將始終為新 Intent 創(chuàng)建新實例。
- FLAG_ACTIVITY_NEW_TASK 在新任務(wù)中啟動 Activity。如果需要啟動的 Activity 存在及需要的任務(wù)也存在,則該任務(wù)會轉(zhuǎn)到前臺并恢復(fù)其最后狀態(tài),同時 Activity 會在 onNewIntent() 中收到新 Intent。
如果已存在目標 Activity 實例,并且目標 Activity 不在棧頂,此時如果再調(diào)用啟動目標 Activity 的代碼,Activity 并不會顯示到前臺,因為 FLAG_ACTIVITY_NEW_TASK 的作用只是在沒有目標 Activity 實例的情況下創(chuàng)建 Activity 實例,并將目標 Activity 入棧,所以存在 Activity 實例的情況下在調(diào)用啟動方法只是將該任務(wù)會轉(zhuǎn)到前臺并恢復(fù)其最后狀態(tài),此時該 Activity 不一定在棧頂
FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP 結(jié)合 FLAG_ACTIVITY_SINGLE_TOP 一起使用時就可以達到 singleTask 的效果,F(xiàn)LAG_ACTIVITY_NEW_TASK 作用為保證目標 Activity 存在于 taskAffinity 指定棧名相同的棧,并將目標 Activity 入棧,F(xiàn)LAG_ACTIVITY_SINGLE_TOP 作用為如果棧中有目標 Activity,則將棧中目標 Activity 之上的 Activity 清除,所以就達到了 singleTask 的效果。
FLAG_ACTIVITY_NEW_TASK 要產(chǎn)生效果的前提一定是要啟動的 Activity 的 taskAffinity 與當前 Activity 的是不相同的,否則就是默認 standard 的效果
- FLAG_ACTIVITY_CLEAR_TASK 標簽,必須與 FLAG_ACTIVITY_NEW_TASK 配合使用,且必須是 taskAffinity 不同的情況下,如果兩個條件不滿足,則為 standard 模式
如果目標 Activity 所在棧不存在,則創(chuàng)建新棧并創(chuàng)建目標 Activity 對象,放入該棧
如果目標 Activity 存在,則將 Activity 及其所在棧中的所有 Activity 全部清空,包括已經(jīng)存在的目標 Activity 對象,然后重新創(chuàng)建目標 Activity ,再放入目標棧中
- FLAG_ACTIVITY_NO_HISTORY 使用該標簽啟動的 Activity 再啟動其他 Activity 之后,該 Activity 會自動消失,即 onDestory 被調(diào)用,不會保留在 Activity 棧中
...
四、任務(wù)棧
getTaskId() 方法用來獲取 Activity 所在的任務(wù)棧 ID
Activity 棧中如果沒有了 Activity 存在則會銷毀,之后再由新 Activity 產(chǎn)生是會進入新的棧中。
在棧中返回時會先將當前棧中的 Activity 返回,全部返回之后則將跳轉(zhuǎn)到該棧的界面所在的棧切換到前臺,這個界面可以是屏幕首頁,可以是其他應(yīng)用
任務(wù)是指在執(zhí)行特定作業(yè)時與用戶交互的一系列 Activity。 這些 Activity 按照各自的打開順序排列在堆棧(即返回棧)中。
返回棧是任務(wù)的一部分 一個任務(wù)包含一個任務(wù)棧,任務(wù)棧中是一系列 Activity
Android 的多任務(wù)管理,即管理多個任務(wù)的前臺后臺變化
singleTask 如果指定 taskAffinity 或者 singleInstance 都是會啟動新的任務(wù)
首頁的啟動圖標作用為 第一:使 Activity 的圖標和標簽顯示在應(yīng)用啟動器中 第二:可以讓用戶在啟動之后可以隨時切換到創(chuàng)建的任務(wù)棧中
通過為 Activity 提供一個以 "android.intent.action.MAIN" 為指定操作、以 "android.intent.category.LAUNCHER" 為指定類別的 Intent 過濾器,您可以將 Activity 設(shè)置為任務(wù)的入口點
第二點非常重要,其完成的任務(wù)是,如果用戶切換其他任務(wù)棧到前臺,則必須為用戶提供方式當離開任務(wù)棧后還能再次切換到指定棧,首頁圖標則有這個功能。
IntentFilter 的匹配規(guī)則
Activity 的 intent-filter 可以有多組,一個 Intent 只要能匹配任何一組 intent-filter 即可成功啟動對應(yīng) Activity
aciont 匹配規(guī)則:intent-filter 中指定一個或多個 action,Intent 中的 Action 必須匹配其中任意一個
intent-filter 中 action 可以多個,Intent 中必須要有唯一的 action,只要在該 Intent 在 intent-filter 存在即可匹配,action 大小寫敏感
intent-filter 中有的 action 最少有一個,否則無法被隱式啟動
Intent 中 action 必須也只能有一個,否則無法找到對應(yīng) Activity
- category 的匹配規(guī)則:Intent 中指定的 category 在 intent-filter 中必須能夠找到
Intent 中有的 intent-filter 中必須有
Intent 中默認有 "ndroid.intent.category.DEFAULT" ,即 intent-filter 中至少有 DEFAULT
data 匹配規(guī)則同 Intent ,intent-filter 中定義了 data,Intent 中必須存在最少一個data可以與 intent-filter 中的任一 data 匹配
data 分為 mimeType 和 URI 兩部分,intent-filter 中 URI 的默認支持 content 和 file ,Intent 中沒有默認 URI 值,如果 intent-filter 中不聲明 URI 和 type,此時 Intent 可以不包含 URI 和 type ,如果 Intent 中定義了 file 或 content 的 URI,intent-filter 中沒有定義,此時 intent-filter 是支持的
Intent 中調(diào)用 setDataAndType 來添加 URI 和 mimeType,setData 和 setType 方法會默認清除另一對象的值,所以同時設(shè)置 data 和 type 時必須使用 setDataAndType 方法
Intent
判斷是否有接收 Intent 的組件,并在有多個響應(yīng)的時候設(shè)置選擇器名稱
兩種方式判斷是否有接收 Intent 的組件,第一種是使用 PackageManager 的 resolve... 系列方法判斷,返回值為 null 或者一個組件,以及使用 PackageManager 的 queryIntent... 系列方法判斷,返回值為可以響應(yīng)的組件集合,可以判斷 service、activity、BroadcastReceiver
第二種是 Intent 的 resolveActivity 方法判斷,如果有則返回該組件,如果沒有返回 null
使用 PackageManager 的方法需要第二個參數(shù),使用 PackageManager.MATCH_DEFAULT_ONLY 常量來剔除 intent-filter 中沒有聲明 默認 category 的響應(yīng),因為 Intent 默認有 category
-
強制使用選擇器來讓用戶選擇需要啟動的 Activity
Intent sendIntent = new Intent(Intent.ACTION_SEND); String title = getResources().getString(R.string.chooser_title); Intent chooser = Intent.createChooser(sendIntent, title); if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); } 過濾器中必須指明至少一個action,Intent 必須有唯一一個 Action,且在過濾器中必須存在
過濾器中的 category 必須多于或等于 activity 啟動組件使用的 intent 中設(shè)置的
過濾器中 data 匹配規(guī)則同 action ,如果 Intent 指明了 URL 而 過濾器中沒有 URL,則判斷結(jié)果為假定過濾器中支持 content 和 file 的 URL
為確保應(yīng)用安全性,啟動Service時,應(yīng)始終使用顯示 Intent,且不要為 Service 聲明過濾器,因為使用隱式 Intent 啟動服務(wù)存在安全隱患,因為不能確定哪些服務(wù)將響應(yīng) Intent,且用戶無法看到哪些服務(wù)已啟動。