App啟動流程和啟動模式
啟動流程
當(dāng)用戶點擊一個App圖標(biāo)時,Click事件會通過Binder IPC機制調(diào)用startActivity(Intent), 最終調(diào)用到ActivityManagerService。在ActivityManagerService中做三件事:
1、通過PackageManager的resolveIntent()收集這個intent對象的指向信息,指向信息被存儲在一個intent對象中。
2、通過grantUriPermissionLocked()方法來驗證用戶是否有足夠的權(quán)限去調(diào)用該intent對象指向的Activity。
3、如果有權(quán)限, ActivityManagerService會檢查這個進程的ProcessRecord是否存在。如果不存在, ActivityManagerService會創(chuàng)建新的進程來實例化目標(biāo)activity。
ActivityManagerService創(chuàng)建新的進程并實例化目標(biāo)activity分為三個步驟。
第一步,創(chuàng)建進程
ActivityManagerService調(diào)用startProcessLocked()方法來創(chuàng)建新的進程,該方法會通過socket通道傳遞參數(shù)給“Zygote”進程。 Zygote孵化自身,并調(diào)用ZygoteInit.main()方法來實例化ActivityThread對象并最終返回新進程的pid。隨后ActivityThread依次調(diào)用Looper.prepareLoop()和Looper.loop()來開啟消息循環(huán)。
第二步,綁定Application
第二步要做的就是將進程和指定的Application綁定起來。這個是通過ActivityThread對象中調(diào)用bindApplication()方法完成的。該方法發(fā)送一個BIND_APPLICATION的消息到消息隊列中,最終通過handleBindApplication()方法處理該消息。然后調(diào)用makeApplication()方法來加載App的classes到內(nèi)存中。
第三步,啟動Activity
經(jīng)過前兩個步驟之后,系統(tǒng)中已經(jīng)擁有了該App的進程。后面的步驟就是從一個已經(jīng)存在的進程中啟動一個新進程的activity了。該步驟實際調(diào)用方法是realStartActivity(),它會調(diào)用application線程對象中的sheduleLaunchActivity()發(fā)送一個LAUNCH_ACTIVITY消息到消息隊列中,通過 handleLaunchActivity()來處理該消息。
經(jīng)過這三個步驟,就成功啟動了一個App。
啟動模式
由上述App啟動流程可以看出,App啟動需要判斷系統(tǒng)中是否有該App的進程,沒有的話就需要創(chuàng)建。故App的啟動模式可以根據(jù)系統(tǒng)中是否存在App的進程分為熱啟動和冷啟動兩種,如下:
冷啟動:App沒有啟動過或App進程被killed, 系統(tǒng)中不存在該App進程。
熱啟動:App進程處于后臺, 系統(tǒng)將其從后臺帶到前臺, 展示給用戶。
Activity啟動模式
任務(wù)棧:
1、程序打開時就創(chuàng)建了一個任務(wù)棧, 用于存儲當(dāng)前程序的activity,所有的activity屬于一個任務(wù)棧。
2、一個任務(wù)棧包含了一個activity的集合, 去有序的選擇哪一個activity和用戶進行交互:只有在任務(wù)棧棧頂?shù)腶ctivity才可以跟用戶進行交互。
3、任務(wù)棧可以移動到后臺, 并且保留了每一個activity的狀態(tài). 并且有序的給用戶列出它們的任務(wù), 而且還不丟失它們狀態(tài)信息。
4、退出應(yīng)用程序時:當(dāng)把所有的任務(wù)棧中所有的activity清除出棧時,任務(wù)棧會被銷毀,程序退出。
任務(wù)棧的缺點:
1、每開啟一次頁面都會在任務(wù)棧中添加一個Activity,而只有任務(wù)棧中的Activity全部清除出棧時,任務(wù)棧被銷毀,程序才會退出,這樣就造成了用,戶體驗差, 需要點擊多次返回才可以把程序退出了。
2、每開啟一次頁面都會在任務(wù)棧中添加一個Activity還會造成數(shù)據(jù)冗余, 重復(fù)數(shù)據(jù)太多, 會導(dǎo)致內(nèi)存溢出的問題(OOM)。
為了解決任務(wù)棧的缺點,我們引入了啟動模式。
啟動模式(launchMode)在多個Activity跳轉(zhuǎn)的過程中扮演著重要的角色,它可以決定是否生成新的Activity實例,是否重用已存在的Activity實例,是否和其他Activity實例公用一個task里。這里簡單介紹一下task的概念,task是一個具有棧結(jié)構(gòu)的對象,一個task可以管理多個Activity,啟動一個應(yīng)用,也就創(chuàng)建一個與之對應(yīng)的task。
啟動模式有如下4種:
- standard
standard模式是默認(rèn)的啟動模式,不用配置android:launchMode屬性即可,當(dāng)然也可以指定值為standard。跳轉(zhuǎn)時不管棧中有沒有已存在的實例,都生成新的實例。 - singleTop
配置屬性android:launchMode=”singleTop”。跳轉(zhuǎn)時系統(tǒng)會先在棧中尋找是否有對應(yīng)activity實例正位于棧頂,如果有則不再生成新的,而是直接使用,否則生成新的實例。 - singleTask
配置屬性android:launchMode=”singleTask”。跳轉(zhuǎn)時如果系統(tǒng)發(fā)現(xiàn)棧中有對應(yīng)的Activity實例,則使此Activity實例之上的其他Activity實例統(tǒng)統(tǒng)出棧,使此Activity實例成為棧頂對象,顯示到幕前,否則生成新的實例。 - singleInstance
配置屬性android:launchMode=”singleInstance”。跳轉(zhuǎn)時會啟用一個新的棧,并將Activity放置于這個新的棧結(jié)構(gòu)中,并保證不再有其他Activity實例進入。
應(yīng)用場景:
singleTop:適合接收通知啟動的內(nèi)容顯示頁面。例如新聞客戶端的新聞內(nèi)容頁面、推送消息處理頁面。
singleTask:適合作為程序入口點。例如瀏覽器的主界面,電商App主頁面。
singleInstance:適合需要與程序分離開的頁面。例如鬧鈴提醒,電商App秒殺提醒。singleInstance不要用于中間頁面,如果用于中間頁面,跳轉(zhuǎn)會有問題,比如:A -> B (singleInstance) -> C,完全退出后,再次啟動,首先打開的是B。
Activity生命周期
常規(guī)生命周期
onCreate : 該方法是在Activity被創(chuàng)建時回調(diào),它是生命周期第一個調(diào)用的方法,我們在創(chuàng)建Activity時一般都需要重寫該方法,然后在該方法中做一些初始化的操作,如通過setContentView設(shè)置界面布局的資源,初始化所需要的組件信息等。 此方法中不應(yīng)該做一些耗時的操作,否則可能會引起黑屏和ANR。
onStart : 此方法被回調(diào)時表示Activity正在啟動,此時Activity已處于可見狀態(tài),只是還沒有在前臺顯示,因此無法與用戶進行交互??梢院唵卫斫鉃锳ctivity已顯示而我們無法看見罷了。
onResume : 當(dāng)此方法回調(diào)時,則說明Activity已在前臺可見,可與用戶交互了(處于前面所說的Active/Running形態(tài)),onResume方法與onStart的相同點是兩者都表示Activity可見,只不過onStart回調(diào)時Activity還是后臺無法與用戶交互,而onResume則已顯示在前臺,可與用戶交互。當(dāng)Activity停止后(onPause方法和onStop方法被調(diào)用),重新回到前臺時也會調(diào)用onResume方法,因此我們也可以在onResume方法中初始化一些資源,比如重新初始化在onPause或者onStop方法中釋放的資源。
onPause : 此方法被回調(diào)時則表示Activity正在停止(Paused形態(tài)),一般情況下onStop方法會緊接著被回調(diào)。有一種情況是onPause方法執(zhí)行后直接執(zhí)行了onResume方法,這屬于比較極端的現(xiàn)象了,這可能是用戶操作使當(dāng)前Activity退居后臺后又迅速地再回到到當(dāng)前的Activity,此時onResume方法就會被回調(diào)。當(dāng)然,在onPause方法中我們可以做一些數(shù)據(jù)存儲或者動畫停止或者資源回收的操作,但是不能太耗時,因為這可能會影響到新的Activity的顯示——onPause方法執(zhí)行完成后,新Activity的onResume方法才會被執(zhí)行。
onStop: 一般在onPause方法執(zhí)行完成直接執(zhí)行,表示Activity即將停止或者完全被覆蓋(Stopped形態(tài)),此時Activity不可見,僅在后臺運行。同樣地,在onStop方法可以做一些資源釋放的操作(不能太耗時)。
onRestart :表示Activity正在重新啟動,當(dāng)Activity由不可見變?yōu)榭梢姞顟B(tài)時,該方法被回調(diào)。這種情況一般是用戶打開了一個新的Activity時,當(dāng)前的Activity就會被暫停(onPause和onStop被執(zhí)行了),接著又回到當(dāng)前Activity頁面時,onRestart方法就會被回調(diào)。
onDestroy :此時Activity正在被銷毀,也是生命周期最后一個執(zhí)行的方法,一般我們可以在此方法中做一些回收工作和最終的資源釋放。
異常生命周期
- 1、相關(guān)的系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重新創(chuàng)建(一般指橫豎屏切換)
正常啟動Activity時,onCreate,onStart,onResume方法都會依次被回調(diào),而如果我們此時把豎屏的Activity人為的調(diào)整為橫屏,我們可以發(fā)現(xiàn)onPause,onSaveInstanceState,onStop,onDestroy,onCreate,onStart,onRestoreInstanceState,onResume依次被調(diào)用,單從調(diào)用的方法我們就可以知道,Activity先被銷毀后再重新創(chuàng)建 - 2、內(nèi)存不足導(dǎo)致低優(yōu)先級的Activity被殺死
基本類似上一種情況
當(dāng)我們不想Activity在屏幕旋轉(zhuǎn)后導(dǎo)致銷毀重建時,可以設(shè)置configChange=“orientation”;當(dāng)SDK版本大于13時,我們還需額外添加一個“screenSize”的值,對于這兩個值含義如下:
orientation:屏幕方向發(fā)生變化,配置該參數(shù)可以解決橫豎屏切換時,Activity重建問題(API<13)
screenSize:當(dāng)設(shè)備旋轉(zhuǎn)時,屏幕尺寸發(fā)生變化,API>13后必須配置該參數(shù)才可以保證橫豎切換不會導(dǎo)致Activity重建。
設(shè)置了這兩個參數(shù)后,當(dāng)橫豎屏切換時,Activity不會再重建并且也不會調(diào)用之前相關(guān)的方法,取而代之的是回調(diào)onConfigurationChanged方法。
Activity與Window/View的關(guān)系
- Window 是什么?
Window 是 Android 中窗口的宏觀定義,主要是管理 View 的創(chuàng)建,以及與 ViewRootImpl 的交互,將 Activity 與 View 解耦。 - Activity 與 PhoneWindow 與 DecorView 之間什么關(guān)系?
一個 Activity 對應(yīng)一個 Window 也就是 PhoneWindow,一個 PhoneWindow 持有一個 DecorView 的實例,DecorView 本身是一個 FrameLayout。
Activity的主要作用是生命周期的管理,Window是一個視圖容器,WindowManager統(tǒng)一管理View。所以Activity和Window的關(guān)聯(lián)主要是體現(xiàn)在生命周期的管理和事件的回調(diào)上,Window和View的關(guān)聯(lián)體現(xiàn)在對View視圖的處理上。
Activity與Fragment
- 生命周期不同
Activity有7個生命周期:onCreate()、onStart()、onRestart()、onResume()、onPause() 、onStop() 、onDestroy();
Fragment有11個生命周期:onAttach() 、onCreate() 、onCreateView() 、onActivityCreate()、onStart()、onResume() 、onPause() 、onStop() 、onDestroyView()、onDestroy()、onDetach();
onAttach()
作用:fragment已經(jīng)關(guān)聯(lián)到activity,這個時候 activity已經(jīng)傳進來了, 獲得activity的傳遞的值 就可以進行 與activity的通信里, 當(dāng)然也可以使用getActivity(),前提是這個fragment已經(jīng)和宿主的activity關(guān)聯(lián),并且沒有脫離,有且只有調(diào)用一次。
onCreate()
系統(tǒng)創(chuàng)建fragment的時候回調(diào),在里面實例化一些變量,這些個變量主要是:當(dāng)暫停、停止的時候想保持的數(shù)據(jù)、只調(diào)用一次。
onCreateView()
第一次使用的時候 fragment會在這上面畫一個layout出來, 為了可以畫控件 要返回一個 布局的view,也可以返回null就什么都沒有顯示。
當(dāng)系統(tǒng)用到fragment的時候 fragment就要返回他的view,越快越好 ,所以盡量在這里不要做耗時操作,比如從數(shù)據(jù)庫加載大量數(shù)據(jù)
onActivityCreated()
當(dāng)Activity中的onCreate方法執(zhí)行完后調(diào)用。當(dāng)執(zhí)行onActivityCreated()的時候 activity的onCreate才剛完成。所以在onActivityCreated()調(diào)用之前activity的onCreate可能還沒有完成,所以不能再onCreateView()中進行與activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面進行。所以呢,這個方法主要是初始化那些你需要你的父Activity或者Fragment的UI已經(jīng)被完整初始化才能初始化的元素。
onStart()
和activity一致,啟動Fragement 啟動時回調(diào),,此時Fragement可見。
onResume()
和activity一致 在activity中運行是可見的。激活, Fragement 進入前臺, 可獲取焦點時激活。
onPause()
和activity一致 其他的activity獲得焦點,這個仍然可見第一次調(diào)用的時候,指的是 用戶 離開這個fragment(并不是被銷毀)
通常用于 用戶的提交(可能用戶離開后不會回來了)
onStop()
和activity一致, fragment不可見的, 可能情況:activity被stopped了或者 fragment被移除但被,加入到回退棧中,一個stopped的fragment仍然是活著的如果長時間不用也會被移除。
onDestroyView()
Fragment中的布局被移除時調(diào)用。表示fragemnt銷毀相關(guān)聯(lián)的UI布局, 清除所有跟視圖相關(guān)的資源。然后這個知識移除視圖 并沒有銷毀而且還沒有脫離activity
onDestroy()
銷毀fragment對象, 跟activity類似了。
onDetach()
Fragment和Activity解除關(guān)聯(lián)的時候調(diào)用。 脫離activity。
所以Fragment比較與Activity來說會更加靈活,因為生命周期多了,可以控制的地方也就多了。 - 使用的靈活性不同
Fragment顯得更加靈活??梢灾苯釉赬ML文件中添加<fragment/>,Activity則不能;Fragment可以在一個界面上靈活的替換一部分頁面,Activity不可以,Activity 只能進行跳轉(zhuǎn)切換。 - 控件加載的方式不同
Fragment的載入是通過OnCreateView的時候通過inflater.inflate()加載布局
Activity的載入是通過OnCreate的時候通過setContentView()加載布局
Fragment 懶加載
為什么需要懶加載?
減少不必要的資源浪費
什么情況下需要懶加載?
使用viewpager+adapter作為應(yīng)用大的布局時
懶加載的實現(xiàn)方式:
1、setUserVisibleHint + onHiddenChanged
2、FragmentTransaction.setMaxLifecycle()