文章目錄
- 1.生命週期
- 返回棧
- 活動生命週期
- Q:說下Activity的生命週期?
- Q:說下活動的生存期/(onStart方法/onStop()方法與onResume()方法/onPause()方法有什麼區別)
- Q:說下Activity處於onPasue()下可以執行那些操作?
- 生命週期的切換過程
- 1.啓動一個Activity
- 2.當一個Activity打開另一個Activity都會回調哪些方法,如果ActivityB是完全透明的呢,如果啓動的是一個對話框Activity呢?
- 3.啓動新Activity後,又返回到舊的Activity
- 4.關閉屏幕/按Home鍵:
- 5.當一個Activity按Home鍵切換到桌面後又回到該Activity回調哪些方法。
- 6.當一個Activity按back鍵回退時回調哪些方法
- Activity的優先級
- Q:(1)優先級低的Activity在內存不足被回收後怎樣做可以恢復到銷燬前狀態?/(2)橫豎屏切換後怎樣做可以恢復到銷燬前狀態?
- Q:談談onSaveInstanceState() 與 onRestoreIntanceState()
- Q:onSaveInstanceState()與onPause()的區別?
- Q:談談橫豎屏切換過程中調用的函數
- Q:如何防止橫豎屏切換(配置改變)時Activity銷燬並切換
- 生命週期的記憶方法
- 2.Activity啓動模式
- 參考鏈接:
提起四大組件之首Activity,想必是無人不知無人不曉,不論多麼初級的android工程師都會比較瞭解,接下來就總結下關於Activity的面試題。
1.生命週期
返回棧
Q:談一下返回棧
首先理解android是使用Task來管理活動,一個Task就是一組存放在棧裏的活動的集合,這個棧就叫做返回棧,每啓動一個新的活動,就會將其放入棧頂,當我們點擊back回退或調用activity的finish函數處於棧頂的活動就會出棧,前一個入棧的活動就會到棧頂,系統總是顯示處於棧頂的活動。
活動生命週期
Q:說下Activity的生命週期?
- onCreate()方法:活動第一次創建的時候被調用,常做初始化的操作,比如加載佈局(setContentView),綁定事件(findViewById)。表示Activity正在創建。
- onStart()方法:活動由不可見到可見的時候被調用,表示Activity正在啓動,此時Activity可見但不在前臺。
- onResume()方法:活動準備好和用戶進行交互時調用。表示Acitivity獲得焦點,此時Activity可見且在前臺。
- onPause()方法:系統準備去啓動或恢復另一個活動時調用。表示Activity正在停止,此時可做存儲數據,停止動畫等操作。
- onStop()方法:在活動完全不可見的時候調用。表示Activity即將停止。
- onDestory()方法:在活動被銷燬之前調用,表示Activity即將銷燬,常做回收工作、資源釋放。
- onRestart()方法:在活動由停止狀態變爲運行狀態之前調用。表示Activity即將重啓。
Q:說下活動的生存期/(onStart方法/onStop()方法與onResume()方法/onPause()方法有什麼區別)
活動的生存期分爲三個:1.完整生存期 2.可見生存期 3.前臺生存期
完整生存期:onCreate()方法與onDestory()都處於完整生存期,一般情況下,Activity會在onCreate()方法中完成各種初始化操作,而在onDestory()方法中完成釋放內存的操作。
可見生存期:onStart()方法與onStop()方法就是可見生存期,Activity對於用戶是可見的,但無法與用戶交互。onStart()方法中對資源進行加載,onStop()方法中對資源進行釋放。
前臺生存期:onResume方法與onPause方法就是前臺生存期,在前臺生存期內,活動處於運行狀態,此時可以與用戶交互。
Q:說下Activity處於onPasue()下可以執行那些操作?
1.用戶返回該Activity,調用onResume()方法,重新running
2.用戶打開了其他Activity,就會調用onStop()方法
3.系統內存不足,擁有更高權限的應用需要內存,該Activity就會被系統回收
4.如果用戶返回到onStop()的Activity又顯示在前臺了,系統會調用
onRestart() -> onStart() -> onResume() 然後重新running
當Activity結束(調用finish()方法)就會調用onDestory()方法釋放所有佔用的資源。
生命週期的切換過程
1.啓動一個Activity
onCreate->onStart->onResume
2.當一個Activity打開另一個Activity都會回調哪些方法,如果ActivityB是完全透明的呢,如果啓動的是一個對話框Activity呢?
A:onPause->B:onCreate->B:onStart->B:onResume->A:onStop
如果ActivityB是完全透明的或對話框Activity則不會調用onStop。
3.啓動新Activity後,又返回到舊的Activity
B:onPause->A:onRestart->A:onStart->A:onResume->B:onStop->B:onDestory
4.關閉屏幕/按Home鍵:
onPause->onStop
5.當一個Activity按Home鍵切換到桌面後又回到該Activity回調哪些方法。
onPause->onStop->onRestart->onStart->onResume
6.當一個Activity按back鍵回退時回調哪些方法
onPause->onStop->onDestory
Activity的優先級
1.可見且可以交互(前臺Acitivity):正在和用戶交互,優先級最高。
2.可見但不可以交互(可見但非前臺Activity):比如當前Activity啓動了一個對話框Activity,當前Activity就是可見但不可以交互。
3.後臺Activity:已經被暫停的Activity,比如執行了onStop,優先級最低。
當系統內存不足,會按照優先級順序從低到高去殺死目標Activity所在的進程。
Q:(1)優先級低的Activity在內存不足被回收後怎樣做可以恢復到銷燬前狀態?/(2)橫豎屏切換後怎樣做可以恢復到銷燬前狀態?
回答:優先級低的Activity在內存不足被回收後重新打開(橫豎屏切換的過程中)會引發Activity重建。
在Activity由於異常情況被終止時,系統會調用onSaveInstanceState方法來保存當前Activity的狀態,該方法調用於onStop之前,與onPause方法沒有時序關係。當異常終止的Activity被重建時,會調用onRestoreInstanceState方法(該方法在onStart之後),並且把Activity銷燬時onSaveInstanceState保存的Bundle對象參數同時傳遞給onCreate方法和onRestoreInstanceState方法。該方法的調用是在onStart之前。因此可通過onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)來判斷Activity是否被重建,並取出數據進行恢復。但需要注意的是,在onCreate取出數據時一定要先判斷savedInstanceState是否爲空。
補充:其中onCreate和onRestoreInstanceState方法來恢復Activity的狀態的區別:
onRestoreInstanceState方法回調則說明bundle對象非空,不需要加非空判斷,而onCreate需要非空判斷。
Q:談談onSaveInstanceState() 與 onRestoreIntanceState()
onSaveInstanceState()
這兩個方法並不是生命週期方法,它們並不一定會被觸發。當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷燬一個Activity時,onSaveInstanceState() 會被調用,該方法的調用在onStop之前,與onPause沒有時序關係。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。因爲在這種情況下,用戶的行爲決定了不需要保存Activity的狀態。
onSaveInstanceState()時機:
(1)用戶按下Home鍵
(2)橫豎屏切換
(3)按下電源按鈕(關閉屏幕顯示)
(4)內存不足導致優先級的Activity被殺死
onRestoreIntanceState()
當被系統異常銷燬的Activity被重建時,會調用onRestoreIntanceState或onCreate方法來恢復,而onRestoreInstance與Oncreate方法中傳入的Bundle對象是銷燬時onSaveInstanceState保存的,onRestoreIntanceState在onStart之後。
Q:onSaveInstanceState()與onPause()的區別?
onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
Q:談談橫豎屏切換過程中調用的函數
要切記這裏活動已經被銷燬了。
onPause->onSaveInstanceState->onStop->onDestory()->onCreate->onStart->onRestoreIntanceState->onResume
Q:如何防止橫豎屏切換(配置改變)時Activity銷燬並切換
通過對AndroidManifest文件的Activity中指定(configChanges)屬性:
android:configChanges = “orientation| screensize”
來避免橫豎屏切換時,Activity的銷燬和重建,而是回調了onCofigurationChanged()方法
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
這裏附上android configChanges的所有屬性解釋
“mcc“ 移動國家號碼,由三位數字組成,每個國家都有自己獨立的MCC,可以識別手機用戶所屬國家。
“mnc“ 移動網號,在一個國家或者地區中,用於區分手機用戶的服務商。
“locale“ 所在地區發生變化。
“touchscreen“ 觸摸屏已經改變。(這不應該常發生。)
“keyboard“ 鍵盤模式發生變化,例如:用戶接入外部鍵盤輸入。
“keyboardHidden“ 用戶打開手機硬件鍵盤
“navigation“ 導航型發生了變化。(這不應該常發生。)
“orientation“ 設備旋轉,橫向顯示和豎向顯示模式切換。
“fontScale“ 全局字體大小縮放發生改變
生命週期的記憶方法
流程圖
參考鏈接
這個onRestart其實應該畫在onStart方法上面。
這裏是想說明這些方法是兩兩對應的。onCreate創建與onDestory銷燬,onStart方法Activity可見與onStop方法Activity不可見,而onResume方法獲得焦點(在前臺)與onPause方法取消焦點(不在前臺)。而onRestart方法是在onStop後再次啓動Activity時調用,下一步會直接調用onStart方法。如果之前Activity調用了onDestory(被銷燬)之後再次啓動就會調用onCreate。
切換過程的比喻
把Activity比作書本。書本現在都放置在書架上,我們想要閱讀一本書(啓動Activity),首先先將書從書架上拿下來(onCreate),然後放在桌子上準備讀(onStart),之後我們打開書本開始讀並做一些筆記(onResume)。
這個時候如果我們想要閱讀另一本書,就要先合上書1(書1:onPause),然後從書架拿另一本書(書2:onCreate),然後放在桌子上並打開(書2:onStart,onResume)。
如果書1被完全蓋住(就調用書1:onStop),如果沒有被完全蓋住就不調用onStop(比如書2是透明的,或書2是彈窗型Activity)。
我們也可以將書1返回書架(onDestory)。
2.Activity啓動模式
1.android提供了四種Activity啓動模式:
標準模式(standard)
棧頂複用模式(singleTop)
棧內複用模式(singleTask)
單例模式(singleInstance)
2. activity的管理是採用任務棧的形式,上文中已經提過。
1.Standard模式(標準模式)
標準模式就是我們最常用的模式,在該模式下,每當啓動一個新的活動,它就會在返回棧中入棧,並處於棧頂的位置。該模式不會考慮活動是否已經存在棧中,每次啓動都會創建該活動的一個新的實例。
下面來看第一行代碼中的例子。
運行程序,然後在FirstActivity中連續點擊兩次按鈕,觀察logcat中打印信息。
可以看到,每點擊一次按鈕就會創建一次FirstActivity的實例,此時返回棧中也會存在3個FirstActivity的實例,因此你需要連按3次Back鍵才能退出程序。
2.singleTop(棧頂複用模式)
如果需要新創建的Activity處於棧頂的話,那麼Activity的實例就不會重建,而是重用棧頂的實例,並回調如下方法:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
流程如下,當棧頂爲ActivityA,而我們又一次要啓動ActivityA,任務棧就不會繼續增加ActivityA的實例,直接重用棧頂的實例。
如果棧頂不是新建的Activity,就會創建Activity新的實例,並放入棧頂。這裏有個問題就是說比如我們重新調用的Activity處於任務棧中,但不位於棧頂的話,還是會創建實例。
下面來看第一行代碼中的例子。
我們讓FirstActivity中的button1通過點擊可以跳轉到SecondActivity。
我們讓SecondActivity的button2通過點擊可以跳轉到FirstActivity。
之後運行程序,在FirstActivity界面點擊按鈕進入到SecondActivity,然後在SecondActivity界面點擊按鈕,又會重新進入到FirstActivity。
當前Activity爲SecondActivity時,任務棧task爲【SecondActivity,FirstActivity】,此時點擊button,跳轉到FirstActivity,棧頂並非爲FirstActivity,於是又會創建FirstActivity,並壓入任務棧中,任務棧task爲【FirstActivity,SecondActivity,FirstActivity】。
應用場景
1.在通知欄點擊收到的通知,然後需要啓動一個Activity,這個Activity就可以用singleTop,否則每次點擊都會新建一個Activity。
2.防止快速多次點擊Activity,多次啓動。
3.singleTask模式(棧內複用模式)
與棧頂複用模式相對的,就是棧內複用模式,即一個棧內只有一個Activity的實例。可以在AndroidMainfest文件的Activity中指定該Activity需要加載到哪個棧中,即singleTask的Activity可以指定想要加載的目標棧。singleTask和taskAffinity配合使用,指定開啓的Activity加入到哪個棧中。
<activity android:name=".Activity1"
android:launchMode="singleTask"
android:taskAffinity="com.lvr.task"
android:label="@string/app_name">
</activity>
關於taskAffinity的值: 每個Activity都有taskAffinity屬性,這個屬性指出了它希望進入的Task。如果一個Activity沒有顯式的指明該Activity的taskAffinity,那麼它的這個屬性就等於Application指明的taskAffinity,如果Application也沒有指明,那麼該taskAffinity的值就等於包名。
下面來看第一行代碼的實例。
繼續使用singleTop中的FirstActivity和SecondActivity,並將FirstActivity的啓動模式更改爲SingleTask模式。
之後在FirstActivity中添加onRestart()方法,並打印日誌。
最後在SecondActivity中添加onDestory()方法,並打印日誌。
重新運行程序,在FirstActivity界面點擊按鈕進入到SecondActivity,然後在SecondActivity界面點擊按鈕,又會重新進入到FirstActivity。
可以看到在SecondActivity中啓動FirstActivity時,會發現棧中已經有了FirstActivity的實例,並且在SecondActivity的下面,於是會讓SecondActivity出棧,而FirstActivity又重新變成棧頂的Actiivity,因此會調用FirstActivity的onRestart方法與SecondActivity的onDestory方法,此時任務棧中只有FirstActivity。下圖很好的說明了這個過程。
其實剛纔說的這種情況前提在於FirstAcitivity指定要加載的棧(taskAffinity)爲默認的棧。
因此該種啓動模式有三種情況:
1.以singleTask啓動ActivityD,同時其所需任務棧(taskAffinity)s1。當前情況下,存在s1棧,但s1棧中沒有ActivityD,直接task1中壓入ActivityD。
2.當前情況與第一行代碼中情況類似。當前情況下,s1棧中存在ActivityB,因此需要將處於ActivityB上面的Activity全部出棧。
3.該情況爲一種特殊情況,我們打算以singleTask啓動ActivityD,比較棧爲s2。而當前存在的棧爲s1,沒有s2任務棧,因此就直接創建一個新的棧,並將Activity壓入。
總結:以SingleTask方式啓動Activity時,會與指定的棧(如果沒有指定就是默認棧)進行比較,如果該棧中存在該Activity就把棧中該Activity之上的Activity全部出棧,如果該棧中不存在該Activity就直接將該Acitivity壓入棧中。
應用場景
應用於首頁或登錄頁,用戶在點擊後退鍵可直接退出應用。
多數App的主頁。對於大部分應用,當我們在主界面點擊回退按鈕的時候都是退出應用,那麼當我們第一次進入主界面之後,主界面位於棧底,以後不管我們打開了多少個Activity,只要我們再次回到主界面,都應該使用將主界面Activity上所有的Activity移除的方式來讓主界面Activity處於棧頂,而不是往棧頂新加一個主界面Activity的實例,通過這種方式能夠保證退出應用時所有的Activity都能被銷燬。
4.singleInstance(單例模式)
一看singleInstance其實就能想到是單例,以該模式啓動Activity,就會直接創建一個新的任務棧,並創建該Activity的實例放入新棧中,一旦該模式的Activity實例已經存在於某個棧中,任何應用激活該Activity時都會重用該棧中的實例。
應用場景
呼叫來電界面。
面試題彙總
Q: 說下Activity的四種啓動模式?
1.standard模式(標準模式):普通啓動模式,每次啓動Activity時,就會創建一個實例。
2.singletop模式(棧頂模式):當啓動Activity時,會判斷任務棧的棧頂是否爲該Activity,如果是該Activity則不會創建實例,去**回調onNewIntent(intent)方法,否則會創建實例
3.singletask模式(棧內模式):當啓動Activity時,只要該Activity在指定的棧中,就不會創建實例,去回調onNewIntent(intent)**方法。如果不存在,會判斷是否指定的棧不存在,就創建一個棧並將Activity的實例壓入,如果指定的棧存在,就直接壓入該棧中。
4.singleInstance模式(單實例模式):該模式下,創建Activity實例時,直接創建一個棧,棧中只有該Activity實例。之後無論哪個應用程序啓動該Activity,都只會調用棧中該實例。
Q:談談singleTop和singleTask的區別以及應用場景
singleTop模式的含義是(參考上面問題),singleTask模式的含義是(參考上面問題),因此二者的差別爲:
singleTop模式:該模式下,任務棧中可能有多個相同Activity實例,因爲它只是判斷當前啓動的Activity是否在棧頂。
該模式的Activity會默認進入啓動它所屬的任務棧,不涉及任務棧的轉換。常用於防止快速連續點擊而創建多個Activity實例。
singleTask模式:該模式向,任務棧中只會有一個Activity實例,因爲它會判斷當前啓動的Activity是否在當前指定的棧中。該模式下Activity可以通過taskAffinity去指定需要的任務棧,可能涉及任務棧的轉換,常用於首頁或登錄頁。因爲不論我們在進入首頁後進入了多少個Activity,當我們返回首頁後,還是希望退出首頁直接可以退出應用。該模式下會把棧中位於要啓動的Activity上面的Activity都出棧。
Q:onNewIntent()調用時機?
有兩個調用時機,分別是singleTop模式下與singleTask模式下啓動Activity。
singleTop模式:當啓動的Activity是在任務棧的棧頂時,會回調onNewIntent方法。
singleTask模式:當啓動的Activity存在於任務棧中,會回調onNewIntent方法。
Q:瞭解哪些Activity啓動模式的標記位?
- FLAG_ACTIVITY_SINGLE_TOP:對應singleTop啓動模式
- FLAG_ACTIVITY_NEW_TASK:對應singleTask模式
Intent啓動與匹配
1.顯式啓動與隱式啓動
- 顯式啓動
構建一個Intent對象,並傳入FirstActivity.this作爲上下文,並傳入SecondActivity.class作爲目標活動
- 隱式啓動
首先在我們需要跳轉的Activity中配置一個< intent-filter>
其中包括< action>標籤與< category>標籤,action標籤代表當前活動可以響應當前action,而category標籤則包含了一些附加信息。只有當Activity中的 < action>標籤與< category>標籤同時匹配intent中的< action>標籤與< category>標籤,這個Activity纔可以響應Intent,也就是說可以跳轉至當前Activity。
下面的代碼是FirstActivity中的部分代碼,通過點擊button1,可以跳轉至SecondActivity。
每個intent只能有一個action,但可以有多個category,假如我們做了如下操作,就無法跳轉,因爲沒有Activity可以響應當前的intent。
隱式啓動的例子
其實隱式啓動有個最直接的例子,就是打開APP後首先顯示的Activity就是隱式啓動。因爲我們爲首先打開的Activiity設置了其intent-filter
標籤,標籤中action爲android.intent.action.MAIN
,category爲android.intent.category.LAUNCHER
。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
另一個例子就是BroadCast的工作機制,不論是靜態註冊BroadCastReceiver,還是動態註冊,都要寫IntentFilter
標籤的,其中包含想要接收廣播的action。
自定義廣播的發送是通過intent來完成的,創建intent,添加action後,使用sendBroadcast發送廣播。
Intent intent = new Intent();
intent.setAction("com.example.aaa.special");
sendBroadcast(intent);
靜態註冊就是在AndroidManifest中給Receiver寫一個IntentFilter標籤,並寫入想要接收廣播的action。
<receiver
android:name=".AnotherReciver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.aaa.special"></action>
</intent-filter>
</receiver>
而動態註冊需要在Activity中創建Recevier的實例,創建一個IntentFilter實例,並通過addAction
添加action。最後使用registerReceiver
方法傳入Recevier實例與IntentFilter實例。
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy(){
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
2.匹配原則
原則:
1.一個intent只有同時匹配某個Activity的intent-filter中的action、category、data纔算完全匹配,才能啓動
該Activity。
2.一個Activity可以有多個 intent-filter,一個 intent只要成功匹配任意一組 intent-filter,就可以啓動
該Activity。
action匹配原則
1.要求intent中的action必須存在且和一組intent-filter中的action完全相等。
2.區分大小寫。
category匹配原則
1.intent可以沒有category,因爲會有默認的值。
2.否則intent中的category要求與那一組intent-filter中的全部category完全相同。
data匹配原則
1.如果intent-filter中有定義data,那麼Intent中也必須也要定義date。
2.data主要由mimeType(媒體類型)和URI組成。在匹配時通過intent.setDataAndType(Uri data, String type)方法對data進行設置。
判斷是否匹配
採用隱式方式啓動Activity時,可以用PackageManager的resolveActivity方法或者Intent的resolveActivity方法判斷是否有Activity匹配該隱式Intent。