Activity的生命周期
Activity的生命周期一般分為兩部分遭庶,一是正常情況下的生命周期宁仔,也就是一般就是這樣,而是異常情況下的生命周期峦睡。
典型情況下
典型情況下是指在有用戶參與的情況下翎苫,activity所經(jīng)過的生命周期的改變。這時activity會經(jīng)歷如下生命周期:
- onCreate:表示activity正在被創(chuàng)建榨了。做初始化工作煎谍,例如調(diào)用setContentView加載界面布局資源、初始化activity所需的數(shù)據(jù)等龙屉。
- onRestart:表示activity正在重新啟動呐粘。一般情況下當(dāng)當(dāng)前activity從不可見到重新變?yōu)榭梢姞顟B(tài)時,會被調(diào)用转捕。這種情形一般是用戶行為所致作岖。
- onStart:表示activity正在被啟動,即將開始五芝。這時的activity已經(jīng)可見了痘儡,但是還沒有出現(xiàn)在前臺,還無法和用戶交互枢步〕辽荆可以理解為:activity已經(jīng)顯示出來了,但是我們還看不到
- onResume:表示activity已經(jīng)可見了醉途,并且出現(xiàn)在前臺并開始活動矾瑰。和onStart的區(qū)別,onStart這時在后臺隘擎,onResume才顯示到前臺
- onPause:表示activity正在停止殴穴。正常情況下,onStop會緊接著被調(diào)用。但有可能這個時候突然快速回到當(dāng)前activity,onResume就會被調(diào)用采幌。
onPause的時候可以做一些存儲數(shù)據(jù)(將數(shù)據(jù)存儲到數(shù)據(jù)庫恍涂,存儲到本地文件等)、停止動畫等工作植榕。這些操作不能太耗時(可以開一個線程)再沧,因為會影響到新activity的顯示。onPause必須先執(zhí)行完尊残,新activity的onResume方法才會執(zhí)行
- onStop:表示activity即將停止炒瘸。可以做一些輕量級的工作寝衫,不可以太耗時顷扩。例如保存頁面上的一個字符串之類的。
- onDestory:表示activity即將被銷毀慰毅。最后一個回調(diào)隘截,可以做一些回收工作和最終資源的釋放。
異常情況下
異常情況下是指activity被系統(tǒng)回收或由于當(dāng)前設(shè)備的configuration發(fā)生改變而導(dǎo)致activity被銷毀重建汹胃。
情況1:資源相關(guān)的系統(tǒng)配置發(fā)生改變
發(fā)生這種情況的典型例子就是屏幕處于豎屏?xí)r突然被旋轉(zhuǎn)婶芭。
我之前寫過一個例子,底部導(dǎo)航欄+fragment旋轉(zhuǎn)后三個fragment重疊在一起了着饥。原因就是activity重建犀农,把三個fragment重新按順序創(chuàng)建并顯示了。解決辦法就是使用onSaveInstanceState保存當(dāng)前所在的fragment頁面宰掉,在旋轉(zhuǎn)后從onRestoreInstanceState()方法中恢復(fù)旋轉(zhuǎn)之前所在頁面呵哨,其他頁面重新創(chuàng)建。就是保留之前所在頁面的fragment對象轨奄。
這兩個保存和恢復(fù)數(shù)據(jù)的方法的調(diào)用流程:
- onPause-->onSaveInstanceState-->onStop
- onstart-->onRestoreInstanceState-->onResume
情況2:資源內(nèi)存不足導(dǎo)致低優(yōu)先級activity被殺死
當(dāng)系統(tǒng)內(nèi)存不足是就會按照優(yōu)先級殺死activity所在的進(jìn)程孟害。
activity的優(yōu)先級有三種:
- 前臺activity:正在和用戶交互的activity,優(yōu)先級最高
- 可見非前臺activity:可見但無法交互。例如:activity上彈出一個對話框
- 后臺activity:不可見挪拟,被暫停挨务,比如執(zhí)行了onStop(),優(yōu)先級最低。
如果一個進(jìn)程中沒有四大組件在執(zhí)行舞丛,那么這個進(jìn)程很快會被系統(tǒng)殺死耘子。因此后臺工作不應(yīng)該脫離四大組件果漾,最好的方法是將后臺工作放在Service中從而保證一定優(yōu)先級球切。
Activity的啟動模式
為什么需要啟動模式
在默認(rèn)情況下,當(dāng)我們多次啟動同一個activity時绒障,系統(tǒng)會創(chuàng)建多個實例并把他們加入到任務(wù)棧中吨凑,當(dāng)我們單擊back鍵的時候,這些activity又會從任務(wù)棧中依次退回。是一種“后進(jìn)先出”的棧結(jié)構(gòu)鸵钝。當(dāng)棧中無任何activity時糙臼,系統(tǒng)就會回收這個任務(wù)棧。這是android的默認(rèn)啟動模式恩商。但是多次啟動同一個activity变逃,系統(tǒng)重復(fù)創(chuàng)建多個實例,這樣是很不合理的怠堪。因此它提供了啟動模式來修改系統(tǒng)的默認(rèn)行為揽乱。
standard 標(biāo)準(zhǔn)模式
系統(tǒng)的默認(rèn)啟動模式。它的啟動具有如下特點:
- 每次啟動一個activity都會創(chuàng)建一個新的實例粟矿,不管這個實例是否已經(jīng)存在
- 被創(chuàng)建的實例的生命周期符合典型情況下activity的生命周期
- 一個任務(wù)棧中有多個實例凰棉,每個實例也可以在不同的任務(wù)棧中(誰啟動了這個activity,這個activity就運行在啟動它的activity的任務(wù)棧中)
當(dāng)用ApplicationContext去啟動standard模式的activity的時候會報錯陌粹。因為standard模式下的activity會進(jìn)入啟動它的activity所在的任務(wù)棧中撒犀,但是非activity類型的context,就像ApplicationContext并沒有所謂的任務(wù)棧,所以就會有問題掏秩。解決問題的方法就是或舞,為待啟動的acitivity指定FLAG_ACTIVITY_NEW_TASK標(biāo)志位,這樣activity啟動的時候就會為它自己創(chuàng)建一個新的任務(wù)棧蒙幻。這個時候的啟動實際是以singleTask啟動的嚷那。
singleTop 棧頂復(fù)用模式
它有如下特點:
- 新activity已經(jīng)位于任務(wù)棧棧頂,此activity不會被重新創(chuàng)建杆煞,同時它的onNewIntent方法會被回調(diào)魏宽,通過此方法的參數(shù)可以取出當(dāng)前請求的信息
- 這個activity的onCreate()、onStart()方法不會被系統(tǒng)調(diào)用
- 新activity實例已存在但不是位于棧頂决乎,新activity仍舊會被重建
singleTask 棧內(nèi)復(fù)用模式
它有如下特點:
- 是一種單例模式队询,只要activity在一個棧中存在,activity多次啟動實例不會重復(fù)創(chuàng)建
- 和singleTop一樣构诚,系統(tǒng)也會回調(diào)onNewIntent方法
- 如果當(dāng)前的activity已被創(chuàng)建且在棧中蚌斩,但不位于棧頂,系統(tǒng)會將此activity之上的activity都出棧范嘱。
例如一個棧中有四個activity從底到頂分別為ADBC,現(xiàn)在要創(chuàng)建D送膳,發(fā)現(xiàn)棧中有,根據(jù)棧內(nèi)復(fù)用原則丑蛤,D不會重新創(chuàng)建叠聋,系統(tǒng)會把D切換到棧頂并調(diào)用其onNewIntent方法。同時由于singleTask默認(rèn)具有clearTop的效果受裹,會導(dǎo)致棧內(nèi)所有在D上面的activity全部出棧碌补,最終棧內(nèi)情況為AD虏束。
流程如下:
singleInstance 單實例模式
它有如下特點:
- 單實例模式。是一種加強(qiáng)的singleTask模式厦章,具有singleTask的所有特性還有一點镇匀,activity只能單的地位于一個任務(wù)棧中。
- 系統(tǒng)會為這個activity創(chuàng)建一個新的任務(wù)棧袜啃,其獨自運行在這個棧中汗侵。根據(jù)棧內(nèi)復(fù)用特性,后續(xù)的請求均不會創(chuàng)建activity,除非這個獨特的任務(wù)棧被銷毀了群发。
一個參數(shù):TaskAffinity
TaskAffinity可以翻譯為任務(wù)相關(guān)性晃择。這個參數(shù)標(biāo)識了一個activity所需要的任務(wù)棧的名字。
可以為每個activity都單獨指定TaskAffnity,這個屬性值必須不能和包名相同也物,否則就沒有意義了宫屠。
TaskAffinity屬性主要和singleTask啟動模式或者allowTaskReparent屬性配對使用,在其他情況下沒有意義滑蚯。
任務(wù)棧分為前臺任務(wù)棧和后臺任務(wù)棧浪蹂,后臺任務(wù)棧中的activity位于暫停狀態(tài),用戶可以通過切換將后臺任務(wù)切換到前臺任務(wù)告材。
當(dāng)TaskAffinity和singleTask同時存在坤次,當(dāng)前activity會運行在名字和TaskAffinity一樣的任務(wù)棧中
在書上看到這的時候?qū)θ蝿?wù)棧分為前臺和后臺不是很理解,android系統(tǒng)會為每一應(yīng)用程序都設(shè)置一個后臺棧么斥赋?前臺棧的東西都移到后臺缰猴?這樣不會太麻煩了吧。疤剑。滑绒。查了下,事實并不是我想的那樣隘膘,當(dāng)前應(yīng)用通過home鍵回到主屏幕的時候疑故,之前的前臺棧就會變成后臺棧。系統(tǒng)會根據(jù)這個應(yīng)用在后臺待的時長去判斷是否只保留最初的MainActivity,還是從棧頂?shù)腶ctivity進(jìn)行恢復(fù)弯菊。
這里有兩個例子纵势,感覺會加深一些理解:
- 應(yīng)用A啟動了應(yīng)用B的某個activity,若這個acitivty的屬性allowTaskReparent = true。那么再次啟動B后管钳,此Activity會直接從A的任務(wù)棧中轉(zhuǎn)移到B的任務(wù)棧中钦铁。
這主要就是屬性allowTaskReparent的功勞,這個屬性大概意思就是允許當(dāng)前任務(wù)從新找一個父母才漆。也就是重新啟動的時候可以到另一棧中牛曹。
- 假設(shè)目前有兩個棧,前臺任務(wù)棧的情況為AB,后臺任務(wù)棧的情況為CD栽烂。假設(shè)CD的啟動模式均為singleTask,現(xiàn)在請求啟動D躏仇。
結(jié)果是后臺任務(wù)都會被切換到前臺,也就是CD任務(wù)都到了前臺任務(wù)棧中腺办,前臺任務(wù)椦媸郑現(xiàn)在的情況為ABCD。
- 假設(shè)現(xiàn)在有三個activity怀喉。activity A的啟動模式為standard,activity B和activity C的啟動模式都為singleTask,且B C的TaskAffnity的屬性都一樣书妻。做如下操作:A啟動B,B啟動C躬拢,C再啟動A躲履,A再啟動B,最后再按兩次返回鍵聊闯,結(jié)果會如何工猜?
結(jié)果是回到了桌面。其實這個問題畫個圖想想就很簡單菱蔬。A啟動B篷帅,A因為是standard模式,所以A會在自己的包名命名的棧中拴泌。而B C因為是singleTask模式魏身,在啟動B的時候就創(chuàng)建好了以B的TaskAffnity命名的棧中,創(chuàng)建C的時候蚪腐,系統(tǒng)發(fā)現(xiàn)已經(jīng)有了所需要的棧箭昵,因此現(xiàn)在棧中有BC。C再啟動A回季,A是standard模式家制,系統(tǒng)會再次創(chuàng)建一個新的A對象,A進(jìn)入啟動它的C所在的棧中泡一,其實棧里有BCA慰丛。再次A啟動B,B有自己的棧瘾杭,且根據(jù)singleTask啟動模式诅病,此時這個棧中有B對象,因此B對象上的所有對象都會彈出粥烁,B執(zhí)行onNewIntent方法贤笆。此時這個棧中只剩下了B。按下back鍵后讨阻,B彈出芥永,任務(wù)棧為空,棧被銷毀钝吮,此時還剩下最初的A所在的棧埋涧,這時候回到后臺任務(wù)棧將A顯示出來板辽,再back就回到了桌面。
通過這三個例子就能進(jìn)一步理解activity的啟動模式棘催。
指定啟動模式的兩種方法
1. 在AndroidMenifest.xml中
<activity
android:name = "com.lxy.test.MainActivity"
android:configChanges="screenLayout"
android:launchMode="singleTask"/>
2. 在Intent中設(shè)置標(biāo)志位
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
這兩種方式的區(qū)別:
- 第二種的優(yōu)先級高于第一種劲弦。兩者同時存在時,以第二種為準(zhǔn)醇坝。
- 限定范圍上有所不同邑跪。第一種無法設(shè)置FLAG_ACTIVITY_CLEAR_TOP標(biāo)志,第二種無法指定singleInstance模式呼猪。
Activity Flags
這里會展示一些比較常用的標(biāo)記位
- FLAG_ACTIVITY_NEW_TASK:指定singleTask模式
- FLAG_ACTIVITY_NEW_TOP:指定singleTop模式
- FLAG_ACTIVITY_CLEAT_TOP:啟動時画畅,位于當(dāng)前activity之上的所有activity都會出棧。這個標(biāo)記位一般默認(rèn)和singleTask模式默認(rèn)出現(xiàn)
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有這個標(biāo)記的activity不會出現(xiàn)在歷史activity的列表中宋距。等同于在xml中的屬性android:excludeFromRecents="true"轴踱。不太懂這個歷史activity列表,是從這個activity到另一個activity,再返回的時候這個activity已經(jīng)不存在了么谚赎,相當(dāng)于被finish了寇僧?在網(wǎng)上沒有查到解釋這個的意思。
IntentFilter匹配規(guī)則
在書上看到這一部分才會想起來沸版,原來IntentFilter是用在隱式調(diào)用的嘁傀。剛開始學(xué)android的時候就學(xué)過這,不過后來因為每次啟動一個活動的時候都用的是顯示啟動把這塊就淡忘了视粮。趁著這個機(jī)會再復(fù)習(xí)一下细办。
首先先向下隱式啟動一般可以用在什么時候,它可以彌補顯示啟動的哪些蕾殴。比如說我現(xiàn)在想調(diào)用百度的網(wǎng)頁笑撞,就可以使用顯示啟動。通過intent的setData將百度網(wǎng)址傳進(jìn)去钓觉,調(diào)用系統(tǒng)瀏覽器就可以打開了茴肥。下面看看這個IntentFilter需要哪些規(guī)則。
intent-filter匹配規(guī)則
- intent-filter中所設(shè)置的過濾信息荡灾,如果不匹配將無法啟動目標(biāo)activity.intent-filter的過濾信息有action瓤狐、category、data.
- 一個過濾列表中的action category data可以有多個批幌,所有的action category data分別構(gòu)成不同的類別础锐。但只有一個Intent同時匹配這三個規(guī)則才算完全匹配。
action匹配規(guī)則
action是一個字符串荧缘,系統(tǒng)預(yù)定義了一些action,同時也可以在應(yīng)用中定義自己的action皆警。
- intent中必須有action
- 匹配是指字符串完全相同,區(qū)分大小寫
- 一個過濾中可以有多個action截粗,intent能夠與任意一個action相同則匹配成功信姓。
- 如果過濾中有action,intent中action不存在時鸵隧,過濾失敗
category匹配規(guī)則
- 如果intent中有category,那么不管有幾個,每個category都必須是intent-filter中定義的
- 如果intent中沒有category,那么必須在intent-filter中指定“android.intent.category.DEFAULT”
- 系統(tǒng)在調(diào)用startActivity和startActivityForResult的時候意推,會默認(rèn)為intent加上“android.intent.category.DEFAULT”
data匹配規(guī)則
data的匹配規(guī)則和action類似豆瘫,如果過濾規(guī)則中定義了data,那么Intent中也要定義可匹配的data。
data語法如下:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
data由兩部分組成左痢,mimeType和URI靡羡。
mimeType
媒體類型:image/jpeg audio/mpeg4-generic video/*等 可以表示圖片系洛、文本俊性、視頻等不同的媒體格式
URI
URI的結(jié)構(gòu):
<scheme:>//host:port/[<path>|<pathPrefix>|<pathPattern>]
實際的例子:
content://com.example.project:200/folder/subfolder/ect
http://www.baidu.com:80/search/info
每個數(shù)據(jù)的含義:
- Scheme:URI模式,比如:http描扯、file定页、content等。若URI中沒有指定這個绽诚,則無效典徊。
- Host:主機(jī)名。
- Port:端口號
- Path恩够、pathPrefix卒落、pathPattern:路徑信息
- path:完整的路徑信息
- pathPattern:可以包含通配符,表示0個或多個任意字符
- pathPrefix:路徑前綴信息
啟動前判斷
通過隱式Intent啟動一個activity的時候蜂桶,可以做下判斷儡毕,看是否有activity能夠匹配隱式intent。
- PackageManager的resolveActivity()/Intent的resolveActivity()
返回最佳匹配的activity信息扑媚,否則返回null腰湾。
- PackageManager的queryIntentActivities()
返回所有成功匹配的activity信息。
public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags)
public ComponentName resolveActivity(@NonNull PackageManager pm)
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)
lags一般填MATCH_DEFAULT_ONLY疆股,表示只匹配intent-filter中聲明default category的Activity费坊。
一般在MainActivity注冊的時候系統(tǒng)都會自動生成這個:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
他們的共同作用是表示這是一個activity的入口,并且會出現(xiàn)在activity的應(yīng)用列表中旬痹,二者缺一不可附井。Service BroadCastReceiver,PackManager同樣提供了類似的方法獲取成功匹配的信息。