Activity
是一種展示型組件隧熙,也是Android
四大組件中唯一一個(gè)用戶能夠直接感知到的焰络。所以對用戶來說Activity
就是一個(gè)Android
應(yīng)用的全部胧后,因此Activity
的重要性不言而喻尊沸,那么下面開始接入正題:
一、生命周期
可以說Activity
的生命周期是面試中必考的基礎(chǔ)知識(shí)早处。Activity
的整個(gè)生命周期涉及到七大方法:onCreate()
湾蔓、onStart()
、onResume()
砌梆、onPause()
默责、onStop()
,onDestory()
和onRestart()
咸包。
下面將詳細(xì)介紹一下這個(gè)七個(gè)方法:
-
onCreate()
當(dāng)Activity被創(chuàng)建時(shí)調(diào)用桃序,其任務(wù)是做初始化工作,如setContentView
界面資源烂瘫,初始化數(shù)據(jù)等媒熊。我們也常在此方法中進(jìn)行界面控件的獲取和相關(guān)事件的綁定。
此方法中有一個(gè)Bundle
類型的參數(shù)用于被異常銷毀重建時(shí)回復(fù)相關(guān)數(shù)據(jù)坟比。有關(guān)Activity的異常銷毀與重建會(huì)在后面講解芦鳍。 -
onStart()
當(dāng)Activity正在啟動(dòng)是調(diào)用,此時(shí)Activity可見但不在前臺(tái)葛账,無法與用戶交互柠衅。 -
onResume()
Activity獲得焦點(diǎn)時(shí)調(diào)用,此時(shí)Activity可見且在前臺(tái)籍琳,此時(shí)可以與用戶交互菲宴。 -
onPause
當(dāng)Activity正在停止時(shí)調(diào)用,此時(shí)Activity可見但不可交互趋急。在這個(gè)方法中我們可以用來做數(shù)據(jù)存儲(chǔ)或停止動(dòng)畫等操作喝峦。 -
onStop()
當(dāng)Activity即將停止時(shí)調(diào)用,此時(shí)Activity是不可見且不可交互的呜达,我們通常會(huì)在此方法中做一些稍微重量級的回收工作谣蠢,如取消網(wǎng)絡(luò)請求,注銷廣播接收器等。
注意:Activity切換時(shí)漩怎,如果新的Activity是透明主題勋颖,那么onStop()
方法不會(huì)執(zhí)行,因?yàn)榇藭r(shí)這個(gè)Activity是可見的勋锤。 -
onDestory()
當(dāng)Activity即將銷毀時(shí)調(diào)用,我們通常在會(huì)在此方法中做一些回收工作以及資源釋放等侥祭。 -
onRestart()
Activity重新啟動(dòng)叁执。一般情況下當(dāng)Activity由不可見重新變?yōu)榭梢姞顟B(tài)時(shí)調(diào)用,或者說在Activity被onStop
后矮冬,但是沒有被onDestroy
谈宛,再次啟動(dòng)此Activity時(shí)就會(huì)調(diào)用onRestart
(而不再調(diào)用onCreate
)方法,此時(shí)Activity由后臺(tái)切換到前臺(tái)胎署,由不可見到可見吆录。
這樣拆開來講可能會(huì)有點(diǎn)抽象不易理解和記憶,所以我們可以通過兩兩組合來記或許會(huì)更容易一些:
-
onCreate()
和onDestory()
可以稱之為完整生命周期琼牧,在onCreate()
中完成各種初始化操作恢筝,onDestory()
中釋放資源; -
onStart()
和onStop()
可以稱之為可見生命周期巨坊,此時(shí)Activity是可見的撬槽,但是無法與用戶進(jìn)行交互; -
onResume
和onPause()
可以稱之為前臺(tái)生命周期趾撵,此時(shí)活動(dòng)可見侄柔,也可以與用戶交互;
下面再放上一張圖片輔助理解和記憶:
推薦閱讀:Activity生命周期之我見占调,上面那張圖就源于此文章暂题,另外這篇文章中還舉了一個(gè)較為形象的例子:我們把一個(gè)Activity比作一本書,那么如果我現(xiàn)在要看一本書A究珊,我需要先從書架取出這本書(onCreate
)薪者,然后放到書桌上(onStart
),接著翻開書(onResume
)苦银,這時(shí)我們就可以開始看了啸胧。如果這時(shí)我們突然想去看書B,那我們就需要先合上書A或者直接走到書架旁(書A的onPause
)幔虏,然后同樣的取出書B(書B的onCreate
)纺念,將書放到書桌上(書B的onStart
),然后翻開書(書B的onResume
)想括,如果此時(shí)書B完全遮蓋住了書A的話陷谱,那么書A的onStop
方法就會(huì)執(zhí)行,如果沒有完全遮蓋住則不會(huì)調(diào)用。
如果這樣還是不能很好的理解的話烟逊,下面再舉出一些例子來強(qiáng)化記憶:
- Activity正常啟動(dòng)流程:
onCreate()
>>onStart()
>>onResume()
渣窜。 - Activity正常結(jié)束流程:
onPause()
>>onStop()
>>onDestory()
。 - 由一個(gè)Activity(FirstActivity)跳轉(zhuǎn)到新的Activity(SecondActivity):FirstActivity.
onPause()
>> SecondActivity.onCreate()
>> SecondActivity.onStart()
>> SecondActivity.onResume()
>> FirstActivity.onStop()
宪躯。 - 由一個(gè)新的Activity(SecondActivity)返回到Activity(FirstActivity):SecondActivity.
onPause()
>> FirstActivity.onRestart()
>> FirstActivity.onStart()
>> FirstActivity.onResume()
>> SecondActivity.onStop()
>> SecondActivity.onDestory()
乔宿。
二、異常情況下的生命周期分析
什么叫異常情況呢访雪,例如手機(jī)內(nèi)存不足了详瑞,那么系統(tǒng)后臺(tái)可能就會(huì)自行銷毀一些目前沒有在用的Activity。此時(shí)就設(shè)置到另外兩個(gè)新方法了:onSaveInstanceState()
和onRestoreInstanceState()
臣缀。這兩個(gè)方法并不是生命周期方法坝橡,所以就并不一定觸發(fā),當(dāng)應(yīng)用遇到意外情況(如:內(nèi)存不足精置、用戶直接按Home鍵)由系統(tǒng)銷毀一個(gè)Activity
時(shí)计寇,onSaveInstanceState()
會(huì)被調(diào)用。但是當(dāng)用戶主動(dòng)去銷毀一個(gè)Activity
時(shí)脂倦,例如在應(yīng)用中按返回鍵番宁,onSaveInstanceState()
就不會(huì)被調(diào)用。因?yàn)樵谶@種情況下狼讨,用戶的行為決定了不需要保存Activity的狀態(tài)贝淤。通常onSaveInstanceState()
只適合用于保存一些臨時(shí)性的狀態(tài),而onPause()
適合用于數(shù)據(jù)的持久化保存政供。
推薦閱讀:onSaveInstanceState和onRestoreInstanceState詳解播聪,Activity詳解(二)——異常情況下的生命周期分析
這里有一個(gè)相對較為常見的實(shí)例:當(dāng)手機(jī)橫豎屏切換時(shí)。此時(shí)會(huì)依次調(diào)用onPause()
布隔、onSaveInstanceState(Bundle outState)
离陶、onStop()
、onDestory()
衅檀、onCreate()
招刨、onStart()
、onRestoreInstanceState(Bundle savedInstanceState)
以及onResume()
方法哀军。
注意:onSaveInstanceState(Bundle outState)
的調(diào)用時(shí)機(jī)在onStop()
之前沉眶,但和onPause()
沒有既定的時(shí)序關(guān)系,即它既可能在onPause()
之前調(diào)用杉适,也可能在其之后調(diào)用谎倔。同樣的,onRestoreInstanceState(Bundle savedInstanceState)
的調(diào)用時(shí)機(jī)在onStart()
之后猿推。
三片习、啟動(dòng)模式
Activity總共有四種啟動(dòng)模式(LaunchMode):
-
standard:標(biāo)準(zhǔn)模式捌肴、默認(rèn)模式
默認(rèn)的啟動(dòng)模式。系統(tǒng)在啟動(dòng)Activity的任務(wù)中創(chuàng)建Activity的新實(shí)例并向其傳送Intent藕咏,即每次啟動(dòng)一個(gè)Activity就會(huì)創(chuàng)建一個(gè)新的實(shí)例状知。Activity可以多次實(shí)例化,而每個(gè)實(shí)例均可屬于不同的任務(wù)孽查,并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例饥悴。 -
singleTop:棧頂復(fù)用模式
如果當(dāng)前任務(wù)的頂部已經(jīng)存在Activity的一個(gè)實(shí)例,則系統(tǒng)會(huì)通過調(diào)用該實(shí)例的onNewIntent()
方法向其傳送Intent盲再,而不是創(chuàng)建Activity的新實(shí)例铺坞,即如果新Activity已經(jīng)位于任務(wù)棧的棧頂,就不會(huì)重新創(chuàng)建洲胖,并回調(diào)onNewIntent(intent)
方法。Activity可以多次實(shí)例化坯沪,而每個(gè)實(shí)例均可屬于不同的任務(wù)绿映,并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例(但前提是位于返回棧頂部的Activity并不是Activity的現(xiàn)有實(shí)例)。
例如:假設(shè)任務(wù)的返回棧中含有A腐晾、B叉弦、C,D四個(gè)Activity(堆棧是A-B-C-D,D位于棧頂)藻糖。收到針對D類Activity的Intent淹冰,如果D具有默認(rèn)的standard
啟動(dòng)模式,則會(huì)啟動(dòng)該類的新實(shí)例巨柒,且堆棧會(huì)變成A-B-C-D-D樱拴。但是如果D的啟動(dòng)模式是singleTop
,則D的現(xiàn)有實(shí)例會(huì)通過onNewIntent()
接收Intent洋满,因?yàn)榇藭r(shí)它位于棧頂晶乔,所以堆棧仍為A-B-C-D。但是如果收到針對B類的Activity的Intent牺勾,則會(huì)向堆棧添加B的新實(shí)例正罢,即時(shí)其啟動(dòng)模式為singleTop
也是如此。 -
singleTask:棧內(nèi)復(fù)用模式
系統(tǒng)創(chuàng)建新任務(wù)并實(shí)例化位于新任務(wù)底部的Activity驻民。但是翻具,如果該Activity的一個(gè)實(shí)例已經(jīng)存在于一個(gè)單獨(dú)的任務(wù)中,則系統(tǒng)會(huì)通過調(diào)用現(xiàn)有實(shí)例的onNewIntent()
方法向其傳送Intent回还,而不是創(chuàng)建新實(shí)例裆泳。一次只能存在Activity的一個(gè)實(shí)例。只要該Activity在一個(gè)任務(wù)棧中存在懦趋,都不會(huì)重新創(chuàng)建晾虑,并回調(diào)onNewIntent(intent)
方法。如果不存在,系統(tǒng)會(huì)先尋找是否存在需要的棧帜篇,如果不存在該棧糙捺,就創(chuàng)建一個(gè)任務(wù)棧,并把該Activity放進(jìn)去笙隙;如果存在洪灯,就會(huì)創(chuàng)建到已經(jīng)存在的棧中。 -
singleInstance:單實(shí)例模式
與singleTask
相同竟痰,只是系統(tǒng)不會(huì)將任何其他的Activity啟動(dòng)到包含啟動(dòng)模式為singleInstance
的實(shí)例的任務(wù)中签钩。該Activity始終是其任務(wù)唯一僅有的成員;由此Activity啟動(dòng)的任何Activity均在單獨(dú)的任務(wù)中打開坏快。
有兩種方式可以設(shè)置Activity的啟動(dòng)模式:
- 在
AndroidManifest.xml
中通過android:launchMode
設(shè)置:
<!--standard singleInstance singleTask singleTop-->
<activity
android:launchMode="standard"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
可選值有standard
singleInstance
singleTask
singleTop
四種铅檩,分別對應(yīng)上述四種啟動(dòng)模式。
- 通過標(biāo)記位設(shè)定莽鸿,方法是
intent.addFlags(Intent.xxx)
:
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(new Intent(MainActivity.this,SecondActivity.class));
其中有兩個(gè)常用的標(biāo)記位:Intent.FLAG_ACTIVITY_SINGLE_TOP
對應(yīng)singleTop
模式和Intent.FLAG_ACTIVITY_NEW_TASK
對應(yīng)singleTask
模式昧旨。
對于singleTop
和singleTask
這兩種啟動(dòng)模式的具體區(qū)別與使用場景推薦閱讀:SingleTop與SingleTask在實(shí)際應(yīng)用中的微妙之處。
簡單來說就是singleTop
和singleTask
都無法用來啟動(dòng)自己祥得。singleTop
多用于為防止快速多次點(diǎn)擊而多次啟動(dòng)Activity兔沃;由于singleTask
模式的Activity重新啟動(dòng)時(shí)會(huì)將覆蓋在其上層的Activity都銷毀掉,所以多用于登錄頁或App的主頁级及。例如QQ退出登錄后進(jìn)入登錄頁面乒疏,當(dāng)你按返回鍵后將會(huì)返回手機(jī)菜單頁面,而不是你點(diǎn)擊退出按鈕的那個(gè)頁面饮焦。
四怕吴、IntentFilter匹配規(guī)則
IntentFilter
直譯過來就是意圖過濾器,我們可以通過它的匹配規(guī)則去打開我們想要打開的一類Activity追驴,例如我們想要打開手機(jī)瀏覽器械哟,但是我們不知道用戶安裝了哪些瀏覽器或者習(xí)慣于使用哪個(gè)瀏覽器,那么我們就可以通過IntentFilter來啟動(dòng)殿雪,讓用戶自己選擇使用哪個(gè)瀏覽器暇咆。
IntentFilter
可以在AndroidManifest.xml
中注冊Activity時(shí)通過<intent-filter>
標(biāo)簽來設(shè)置intentFilter,它有3個(gè)標(biāo)簽屬性action
,category
和data
。
<activity
android:launchMode="standard"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
在說intentFilter
的匹配規(guī)則前丙曙,有必要得先講一下Activity的調(diào)用模式爸业,注意是調(diào)用模式而不是啟動(dòng)模式。Activity的調(diào)用模式有兩種:顯式調(diào)用
和隱式調(diào)用
亏镰。
-
顯式調(diào)用
大多數(shù)情況下我們最常接觸到的就是顯式調(diào)用
了:
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
其實(shí)嚴(yán)格來講扯旷,這個(gè)也不算是顯式調(diào)用,因?yàn)樵陲@式調(diào)用的意義中需要明確之處被啟動(dòng)的對象的組件信息索抓,包括包名和類名钧忽,這里并沒有之處包名:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName("com.mg.axe.testappa","com.mg.axe.testappa.MainActivity");
intent.setComponent(cn);
startActivity(intent);
-
隱式調(diào)用
需要Intent能匹配目標(biāo)組件的IntentFilter中所設(shè)置的過濾信息.如果不匹配將無法啟動(dòng)目標(biāo)Activity毯炮。
示例:
Intent intent = new Intent();
intent.setAction("android.intent.action.View");
startActivity(intent);
當(dāng)我們進(jìn)行Activity的隱式調(diào)用
時(shí),IntentFilter
就可以排上用場了耸黑,那么下面將詳細(xì)介紹其匹配規(guī)則:
-
Action的匹配規(guī)則
Intent中的action
必須能夠和Activity過濾規(guī)則中的Action完全匹配(即完全相等)桃煎。一個(gè)過濾規(guī)則中有多個(gè)action,那么只要Intent中的action
能夠和Activity過濾規(guī)則中的任何一個(gè)action
相同即可匹配成功大刊。簡單的說就是Intent
中的action
必須出現(xiàn)在目標(biāo)Activity的過濾規(guī)則中为迈。
示例:
<!--SecondActivity的intent-filter-->
<intent-filter>
<category android:name = "android.intent.category.DEFAULT" />
<action android:name="com.axe.mg.what" />
</intent-filter>
<!--ThirdActivity的intent-filter-->
<intent-filter>
<category android:name = "android.intent.category.DEFAULT" />
<action android:name="com.axe.mg.what" />
<action android:name="com.axe.mg.how"/>
</intent-filter>
<!--FourthActivity的intent-filter-->
<intent-filter>
<category android:name = "android.intent.category.DEFAULT" />
<action android:name="com.axe.mg.why" />
<action android:name="com.axe.mg.how"/>
</intent-filter>
Intent intent = new Intent();
intent.setAction("com.axe.mg.what");
startActivity(intent);
這種啟動(dòng)方式既可以啟動(dòng)SecondActivity,也可以啟動(dòng)ThirdActivity,但是無法啟動(dòng)FourthActivity缺菌。且必須至少含有一個(gè)<category android:name = "android.intent.category.DEFAULT" />
標(biāo)簽葫辐,否則系統(tǒng)會(huì)拋出ActivityNotFoundException
的異常
-
category的匹配規(guī)則
一個(gè)Intent可以設(shè)置多個(gè)category
,且Intent中的所有category
都必須匹配到Activity中伴郁。也可以不設(shè)置category
垮抗,這時(shí)系統(tǒng)會(huì)自動(dòng)匹配android.intent.category.DEFAULT
鸭津。這里可能感覺和action
很像椒拗,但是只要稍微注意一下就可以發(fā)現(xiàn)Intent是setAction
和addCategory
旷偿,也就是說action
只有一個(gè)(注意是一個(gè)Intent
只有一個(gè)action
痴脾,但是一個(gè)Activity的intent-filter中可以有多個(gè)action
)发乔,而category
可以有很多個(gè)且所有的category
都必須出現(xiàn)在Activity的category
集中垄惧。
示例:
<!--SecondActivity的intent-filter-->
<intent-filter>
<action android:name="com.axe.mg.what" />
<category android:name="com.yu.hu.category1"/>
<category android:name="com.yu.hu.category2"/>
<category android:name = "android.intent.category.DEFAULT" />
</intent-filter>
<!--ThirdActivity的intent-filter-->
<intent-filter>
<action android:name="com.axe.mg.what" />
<category android:name = "android.intent.category.DEFAULT" />
<category android:name="com.yu.hu.category1"/>
<category android:name="com.yu.hu.category2"/>
<category android:name="com.yu.hu.category3"/>
</intent-filter>
<!--FourthActivity的intent-filter-->
<intent-filter>
<action android:name="com.axe.mg.what" />
<category android:name = "android.intent.category.DEFAULT" />
<category android:name="com.yu.hu.category2"/>
</intent-filter>
Intent intent = new Intent();
intent.addCategory("com.yu.hu.category1");
intent.addCategory("com.yu.hu.category2");
intent.setAction("com.yu.hu.what");
startActivity(intent);
此時(shí)依然只能匹配到前兩個(gè)Activity遏片,因?yàn)镕ourthActivity沒有category1
薯嗤。
另外這里還有兩點(diǎn)要注意:
- 因?yàn)閺?qiáng)制要求一個(gè)Activity需要一個(gè)
<category android:name="android.intent.category.DEFAULT"/>
顽爹,所以我們不用將這個(gè)categoty
添加到intent中去匹配。 - 如果單獨(dú)只
addCategory
是沒有用的骆姐,必須setAction
之后才行镜粤。
-
data的匹配規(guī)則
首先來說一下data
的結(jié)構(gòu),data
由兩部分組成:mineType
和URI
玻褪。mineType
指媒體類型肉渴,如.png
.jpg
等。而URI
可配置更多信息:
- scheme:URI的模式带射,如
http
同规。如果URI中沒有指定scheme
,那么整個(gè)URI無效窟社。默認(rèn)為content
和file
券勺。- host:URI的
host
(域名、網(wǎng)址)灿里,如www.baidu.com
关炼。如果指定了scheme
和port
,path
等其他參數(shù)匣吊,但是host
未指定儒拂,那么整個(gè)URI無效寸潦;如果只指定了scheme
,沒有指定host
和其他參數(shù)社痛,URI是有效的见转。- port:URI端口,當(dāng)URI指定了
scheme
和host
參數(shù)時(shí)port
參數(shù)才有意義褥影。- path:用來匹配完整的路徑池户,如:http://example.com/blog/abc.html,這里將 path 設(shè)置為 /blog/abc.html 才能夠進(jìn)行匹配凡怎;
- pathPrefix:用來匹配路徑的開頭部分校焦,拿上面的 URI 來說,這里將
pathPrefix
設(shè)置為 /blog 就能進(jìn)行匹配了统倒;- pathPattern:用表達(dá)式來匹配整個(gè)路徑寨典。
總的來說有點(diǎn)像是正則表達(dá)式,用于匹配指定字段內(nèi)容房匆。
示例:假如我想要匹配https://www.baidu.com:8080/imgs/*
耸成,那么data
應(yīng)該這么寫:
<intent-filter>
<action android:name="xx" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.baidu.com"
android:pathPrefix="/imgs"
android:port="8080"
android:scheme="https" />
</intent-filter>
java代碼:
Intent intent = new Intent();
intent.setData(Uri.parse("https://www.baidu.com:8080/imgs/img1.png"));
startActivity(intent);