前言
雖然有很多面試的文章裏都有這些題目,但是我每次在看的時候,總是會覺得有些分散,複習的時候還要重新去找到對應的文章,所以我就想着自己來整理一下,並且把題目給分一下類型;自己整理可以幫助我複習的同時還可以鞏固一遍;這次主要是4大組件相關,後續我會繼續整理,覺得有幫助的可以點個贊。
今天先分享的是關於Activity的面試題:
描述一下Activity 生命週期?
- onCreate() Activity第-次被創建的時候調用,一些初始化操作可以在這裏完成。
- onStart() 這個方法在Activity 由不可見變爲可見的時候調用。
- onResume() 這個方法在Activity 準備好和用戶進行交互的時候調用。此時的Acivity一定位於返回棧的棧頂,並且處於運行狀態。
- onPause() 這個方法在系統準備去啓動或者恢復另-個Activity的時候調用。
- onStop() 這個方法在Activity 完全不可見的時候調用。它和onPause()方法的主要區別在於,如果啓動的新Activity 是一個對話框式的Activity,那麼onPause()方法會得到執行,而onStop()方法並不會執行。
- onDestroy() 這個方法在Activity被銷燬之前調用,之後Activity的狀態將變爲銷燬狀態。
- onRestart 這個方法在Activity由停止狀態變爲運行狀態之前調用,也就是Activity被重新啓動了。
生命週期:爲了鞏固記憶,畫了一遍。
Activity之間跳轉時的生命週期
A Activity 打開 B Activity 時都有哪些生命週期回調?
A.onPause -> B.onCrete -> B.onStart -> B.onResume -> A.onStop
這樣回答只是及格,因爲僅在 B Activity 的 launchMode 爲 standard 或者 B Activity 沒有可複用的實例時是這樣的。
當 B Activity 的 launchMode 爲 singleTop 且 B Activity 已經在棧頂時(一些特殊情況如通知欄點擊、連點),此時只有 B 頁面自己有生命週期變化:
B.onPause -> B.onNewIntent -> B.onResume
當 B Activity 的 launchMode 爲 singleInstance ,singleTask 且對應的 B Activity 有可複用的實例時,生命週期回調是這樣的:
A.onPause -> B.onNewIntent -> B.onRestart -> B.onStart -> B.onResume -> A.onStop -> ( 如果 A 被移出棧的話還有一個 A.onDestory)
Activity的啓動模式
有4種啓動模式:
- standard 標準模式
- singleTop 棧頂複用模式
- singleTask 棧內複用模式
- singleInstance 單例模式
標準模式:每次啓動時,都會創建一個新的實例在棧頂
棧頂複用模式:如果需要新創建的實例就在棧頂,那麼就不會去重建,而是重用,否則就重新創建。
棧內複用模式:如果實例在當前棧中已經存在,就會將當前實例上面的其他實例都移除棧。
單例模式:直接創建一個新的棧並且創建實例放在棧中。
使用方式:
1.可以在在AndroidMainifest的Activity配置進行設置:android:launchMode="啓動模式"
2.通過 Intent設置標誌位
val intent=Intent(this,SocendActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
//其中標誌位屬性
FLAGACTIVITYSINGLE_TOP:指定啓動模式爲棧頂複用模式( SingleTop)
FLAGACTIVITYNEW_TASK: 指定啓動模式爲棧內複用模式( SingleTask)
FLAGACTIVITYCLEAR_TOP:所有位於其上層的Activity都要移除, SingleTask模式默認具有此標記效果
FLAGACTIVITYEXCLUDEFROMRECENTS:具有該標記的Activity不會出現在歷史Activity的列表中,即無法通過歷史列表回到該Activity上
那麼這兩種方式有什麼區別呢?
- 優先級不同 Intent設置方式的優先級 > Manifest設置方式,即 以前者爲準
- 限定範圍不同 Manifest設置方式無法設定 FLAG_ACTIVITY_CLEAR_TOP; Intent設置方式 無法設置單例模式( SingleInstance)
onStart,onStop和onResume,onPause的區別?
Activity的生命週期中,大部分都是兩兩相對的,可以將其分爲3種,前臺,可見,後臺。 onStart,onStop之間所經歷的是可見的,但是卻可能無法與用戶交互。 onResume,onPause之間所經歷的是屬於前臺,這時候用戶是可以交互的。
如果新Activity是透明主題時,舊Activity會不會走onStop?
不會!
鎖定屏與解鎖屏幕,Activity如何執行生命週期的?
鎖屏時只會調用onPause(),而不會調用onStop方法,開屏後則調用onResume()。
橫豎屏切換時的生命週期?
如果清單文件中沒有設置android:configChanges屬性時,生命週期:先銷燬onPause()、onStop()、onDestroy()再重新創建onCreate()、onStart()、onResume()方法。
設置orientation|screenSize(一定要同時出現)屬性值時,不走生命週期方法,只會執行onConfigurationChanged()方法。
彈出 Dialog 對生命週期有什麼影響?
我們知道,生命週期回調都是 AMS 通過 Binder 通知應用進程調用的;而彈出 Dialog、Toast、PopupWindow 本質上都直接是通過 WindowManager.addView() 顯示的(沒有經過 AMS),所以不會對生命週期有任何影響。
如果是啓動一個 Theme 爲 Dialog 的 Activity , 則生命週期爲:
A.onPause -> B.onCrete -> B.onStart -> B.onResume
注意:這邊沒有前一個 Activity 不會回調 onStop,因爲只有在 Activity 切到後臺不可見纔會回調 onStop;而彈出 Dialog 主題的 Activity 時前一個頁面還是可見的,只是失去了焦點而已所以僅有 onPause 回調。
Activity 在 onResume 之後才顯示的原因是什麼?
雖然我們設置 Activity 的佈局一般都是在 onCreate 方法裏調用 setContentView 。裏面是直接調用 window 的 setContentView,創建一個 DecorView 用來包住我們創建的佈局。詳情如下:
PhoneWindow.java
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
...
// 加載佈局,添加到 mContentParent
// mContentParent 又是 DecorView 的一個子佈局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
然而這一步只是加載好了佈局,生成一個 ViewTree , 具體怎麼把 ViewTree 顯示出來,答案就在下面:
ActivityThread.java
public void handleResumeActivity(...){
// onResume 回調
ActivityClientRecord r = performResumeActivity(...)
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);// 重點
}
}
WindowManager 的 addView 方法最終將 DecorView 添加到 WMS ,實現繪製到屏幕、接收觸屏事件。具體的調用鏈如下:
WindowManagerImpl.addView
-> WindowManagerGlobal.addView
-> ViewRootImpl.setView
-> ViewRootImpl.requestLayout() // 執行 View 的繪製流程
// 通過 Binder 調用 WMS ,WMS 會添加一個 Window 相關的對象
// 應用端通過 mWindowSession 調用 WMS
// WMS 通過 mWindow (一個 Binder 對象) 調用應用端
mWindowSession.addToDisplay(mWindow)
綜上,在onResume回調之後,會創建一個 ViewRootImpl ,有了它之後應用端就可以和 WMS 進行雙向調用了。
onActivityResult 在哪兩個生命週期之間回調?
onActivityResult 不屬於 Activity 的生命週期,一般被問到這個問題時大家都會懵逼。
其實答案很簡單,onActivityResult 方法的註釋中就寫着答案:
「You will receive this call immediately before onResume() when your activity is re-starting.」
跟一下代碼(TransactionExecutor.execute 有興趣的可以自己打斷點跟一下),會發現 onActivityResult 回調先於該 Activity 的所有生命週期回調,從 B Activity 返回 A Activity 的生命週期調用爲:
B.onPause -> A.onActivityResult -> A.onRestart -> A.onStart -> A.onResume
onCreate 方法裏寫死循環會 ANR 嗎?
ANR 的四種場景:
- Service TimeOut: service 未在規定時間執行完成:前臺服務 20s,後臺 200s
- BroadCastQueue TimeOut: 未在規定時間內未處理完廣播:前臺廣播 10s 內, 後臺 60s 內
- ContentProvider TimeOut: publish 在 10s 內沒有完成
- Input Dispatching timeout: 5s 內未響應鍵盤輸入、觸摸屏幕等事件
我們可以看到,Activity 的生命週期回調的阻塞並不在觸發 ANR 的場景裏面,所以並不會直接觸發 ANR。
只不過死循環阻塞了主線程,如果系統再有上述的四種事件發生,就無法在相應的時間內處理從而觸發 ANR。
onNewIntent是什麼時候調用的?
如果需要啓動的實例是之前有打開過的,並且在棧的頂部,目前處於onPause、onStop 的狀態,其他實例再次進入的話,執行順序爲:onNewIntent,onRestart,onStart,onResume。
onSaveInstanceState()方法的作用?
異常情況下系統配置發生改變時導致Activity被殺死並重新創建、資源內存不足導致低優先級的Activity被殺死
- 系統會調用onSaveInstanceState來保存當前Activity的狀態,此方法調用在onStop之前,與onPause沒有既定的時序關係;
- 當Activity被重建後,系統會調用onRestoreInstanceState,並且把onSave(簡稱)方法所保存的Bundle對象同時傳參給onRestore(簡稱)和onCreate(),因此可以通過這兩個方法判斷Activity是否被重建,調用在onStart之後;
Activity跟window,view之間的關係?
- Activity在創建時會調用 attach() 方法初始化一個PhoneWindow(繼承於Window),每一個Activity都包含了唯一一個PhoneWindow
- Activity通過setContentView實際上是調用的getWindow().setContentView將View設置到PhoneWindow上而PhoneWindow內部是通過WindowManager的addView、removeView、updateViewLayout這三個方法來管理View,WindowManager本質是接口,最終由WindowManagerImpl實現
App的啓動流程
1、點擊桌面App圖標,Launcher進程採用Binder IPC向system_server進程發起startActivity請求;
2、system_server進程接收到請求後,向zygote進程發送創建進程的請求;
3、Zygote進程fork出新的子進程,即App進程;
4、App進程,通過Binder IPC向sytem_server進程發起attachApplication請求;
5、system_server進程在收到請求後,進行一系列準備工作後,再通過binder IPC向App進程發送scheduleLaunchActivity請求;
6、App進程的binder線程(ApplicationThread)在收到請求後,通過handler向主線程發送LAUNCH_ACTIVITY消息;
7、主線程在收到Message後,通過發射機制創建目標Activity,並回調Activity.onCreate()等方法。
最後分享一波面試複習路線
多餘的話就不講了,接下來將分享面試的一個複習路線,如果你也在準備面試但是不知道怎麼高效複習,可以參考一下我的複習路線,有任何問題也歡迎一起互相交流,加油吧!
這裏給大家提供一個方向,進行體系化的學習:
1、看視頻進行系統學習
前幾年的Crud經歷,讓我明白自己真的算是菜雞中的戰鬥機,也正因爲Crud,導致自己技術比較零散,也不夠深入不夠系統,所以重新進行學習是很有必要的。我差的是系統知識,差的結構框架和思路,所以通過視頻來學習,效果更好,也更全面。關於視頻學習,個人可以推薦去B站進行學習,B站上有很多學習視頻,唯一的缺點就是免費的容易過時。
另外,我自己也珍藏了好幾套視頻,有需要的我也可以分享給你。
2、進行系統梳理知識,提升儲備
客戶端開發的知識點就那麼多,面試問來問去還是那麼點東西。所以面試沒有其他的訣竅,只看你對這些知識點準備的充分程度。so,出去面試時先看看自己複習到了哪個階段就好。
系統學習方向:
架構師築基必備技能:深入Java泛型+註解深入淺出+併發編程+數據傳輸與序列化+Java虛擬機原理+反射與類加載+動態代理+高效IO
Android高級UI與FrameWork源碼:高級UI晉升+Framework內核解析+Android組件內核+數據持久化
360°全方面性能調優:設計思想與代碼質量優化+程序性能優化+開發效率優化
解讀開源框架設計思想:熱修復設計+插件化框架解讀+組件化框架設計+圖片加載框架+網絡訪問框架設計+RXJava響應式編程框架設計+IOC架構設計+Android架構組件Jetpack
NDK模塊開發:NDK基礎知識體系+底層圖片處理+音視頻開發
微信小程序:小程序介紹+UI開發+API操作+微信對接
Hybrid 開發與Flutter:Html5項目實戰+Flutter進階
知識梳理完之後,就需要進行查漏補缺,所以針對這些知識點,我手頭上也準備了不少的電子書和筆記,這些筆記將各個知識點進行了完美的總結。
3、讀源碼,看實戰筆記,學習大神思路
“編程語言是程序員的表達的方式,而架構是程序員對世界的認知”。所以,程序員要想快速認知並學習架構,讀源碼是必不可少的。閱讀源碼,是解決問題 + 理解事物,更重要的:看到源碼背後的想法;程序員說:讀萬行源碼,行萬種實踐。
主要內含微信 MMKV 源碼、AsyncTask 源碼、Volley 源碼、Retrofit源碼、OkHttp 源碼等等。
4、面試前夕,刷題衝刺
面試的前一週時間內,就可以開始刷題衝刺了。請記住,刷題的時候,技術的優先,算法的看些基本的,比如排序等即可,而智力題,除非是校招,否則一般不怎麼會問。
關於面試刷題,我個人也準備了一套系統的面試題,幫助你舉一反三:
總結
改變人生,沒有什麼捷徑可言,這條路需要自己親自去走一走,只有深入思考,不斷反思總結,保持學習的熱情,一步一步構建自己完整的知識體系,纔是最終的制勝之道,也是程序員應該承擔的使命。
以上內容均免費分享給大家,需要完整版的朋友,點這裏可以看到全部內容。或者關注主頁掃描加 微信 獲取。