( ^▽^)常常發現學着學着,因爲學習的東西愈來愈多,接觸的東西愈來愈多,逐漸的吧本身的最基礎的東西忘得差很少了(o(╥﹏╥)o我也差很少忘了不少東西了)
發現越優秀的人 越注重細節,基礎更加紮實和鞏固
請查看完整的PDF版
(更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
能夠點擊關於我聯繫我獲取完整PDF
(VX:mm14525201314)android
Activity 實際上只是一個與用戶交互的接口而已git
Active/Paused/Stopped/Killed程序員
Activie: 當前 Activity 正處於運行狀態,指的是當前 Activity 獲取了焦點。github
Paused: 當前 Activity 正處於暫停狀態,指的是當前 Activity 失去焦點,此時的 Activity並無被銷燬,內存裏面的成員變量,狀態信息等仍然存在,固然這個 Activity 也仍然可見,可是焦點卻不在它身上,好比被一個對話框形式的 Activity 獲取了焦點,或者被一個透明的 Activity 獲取了焦點,這都能致使當前的 Activity 處於 paused 狀態。面試
Stopped: 與 paused 狀態類似,stopped 狀態的 Activity 是徹底不可見的,可是內存裏面的成員變量,狀態信息等仍然存在,可是也沒有被銷燬。服務器
Killed: 已經被銷燬的 Activity 才處於 killed 狀態,它的內存裏面的成員變量,狀態信息等都會被一併回收。數據結構
正常狀況下的生命週期:Activity 啓動
–>onCreate()
–>onStart()
–>onResume()
點擊 home 鍵回到桌面–>onPause()
–>onStop()
再次回到原 Activity
–>onRestart()
–>onStart()
–>onResume()
退出當前 Activity
時–>onPause()
–>onStop()
–>onDestroy()
app
詳細生命週期以下:
1.啓動了一個 Activity,一般是 Intent 來完成。啓動一個 Activity 首先要執行的回調函數是onCreate()
,一般在代碼中你須要在此函數中綁定佈局,綁定控件,初始化數據等作一些初始化的工做。函數
2.即將執行 Activity 的 onStart()
函數,執行以後 Activity 已經可見,可是尚未出如今前臺,沒法與用戶進行交互。這個時候一般 Activity 已經在後臺準備好了,可是就差執行onResume()
函數出如今前臺。佈局
3.即將執行 Activity 的 onResume()
函數,執行以後 Activity 不止可見並且還會出如今前臺,能夠與用戶進行交互啦。
4.因爲 Activity 執行了 onResume()
函數,因此 Activity 出如今了前臺。也就是 Activity處於運行狀態。
5.處於運行狀態的 Activity 即將執行 onPause()
函數,什麼狀況下促使 Activity 執行onPause()
方法呢?
能夠理解爲當須要其餘 Activity,當前的 Activity 必須先把手頭的工做暫停下來,再來把當前的界面空間交給下一個須要界面的Activity,而 onPause()
方法能夠看做是一個轉接工做的過程,由於屏幕空間只有那麼一個,每次只容許一個 Activity 出如今前臺進行工做。一般狀況下 onPause()函數不會被單獨執行,執行完 onPause()方法後會繼續執行onStop()
方法,執行完 onStop()
方法才真正意味着當前的 Activity 已經退出前臺,存在於後臺。
6.Activity 即將執行 onStop()
函數,在「5」中已經說得很清楚了,當 Activity 要從前臺切換至後臺的時候會執行,好比:用戶點擊了返回鍵,或者用戶切換至其餘 Activity 等
7.當前的 Activity 即將執行 onDestory()
函數,表明着這個 Activity 即將進入生命的終結點,這是 Activity 生命週期中的最後一次回調生命週期,咱們能夠在onDestory()
函數中,進行一些回收工做和資源的釋放工做,好比:廣播接收器的註銷工做等。
8.執行完 onDestory()
方法的 Activity 接下來面對的是被 GC
回收,宣告生命終結
9.不多狀況下 Activity 才走「9」,網上一些關於對話框彈出後 Activity 會走「9」的說法,通過筆者驗證,在某個 Activity 內彈出對話框並無走「9」,因此網上大部分這樣說法的文章要麼是沒驗證,要麼直接轉載的,這個例子說明,實驗出真知,好了,不廢話了,那麼什麼狀況下,Activity 會走「9」呢?
10.當用戶在其餘的 Activity 或者桌面回切到這個 Activity 時,這個 Activity 就會先去執行onRestart()
函數,Restart 有「從新開始」的意思,而後接下來執行 onStart()
函數,接着執行 onResume()
函數進入到運行狀態。
11.在「10」中講的很清楚了。
12.高優先級的應用急須要內存,此時處於低優先級的此應用就會被 kill 掉。
13.用戶返回原 Activity。
下面來着重說明一下 Activity 每一個生命週期函數:onCreate()
:
表示 Activity 正在被建立,這是 Activity 生命週期的第一個方法。一般咱們程序員要在此函數中作初始化的工做,好比:綁定佈局,控件,初始化數據等。
onStart()
:
表示 Activity 正在被啓動,這時候的 Activity 已經被建立好了,徹底過了準備階段,可是沒有出如今前臺,須要執行 onResume()
函數才能夠進入到前臺與用戶進行交互。
onResume()
:
表示 Activitiy 已經可見了,而且 Activity 處於運行狀態,也就是 Activity 不止出如今了前臺,並且還可讓用戶點擊,滑動等等操做與它進行交互。
onPause()
:
表示 Activity 正在暫停,大多數狀況下,Activity 執行完 onPause()
函數後會繼續執行onStop()
函數,形成這種函數調用的緣由是當前的 Activity 啓動了另一個 Activity 或者回切到上一個 Activity。還有一種狀況就是 onPause()
函數被單獨執行了,並無附帶執行 onStop()
方法,形成這種函數調用的緣由很簡單,就是當前 Activity 裏啓動了相似於對話框的東東。
onStop()
:
表示 Activity 即將中止,咱們程序員應該在此函數中作一些不那麼耗時的輕量級回收操做。
onRestart()
:
表示 Activity 正在從新啓動。通常狀況下,一個存在於後臺不可見的 Activity 變爲可見狀態,都會去執行 onRestart()
函數,而後會繼續執行 onStart()
函數,onResume()
函數出如今前臺而且處於運行狀態。
onDestory()
:
表示 Activity 要被銷燬了。這是 Activity 生命中的最後一個階段,咱們能夠在onDestory()
函數中作一些回收工做和資源釋放等,好比:廣播接收器的註銷等。
異常狀況下的生命週期:
什麼是異常狀況呢?
狀況 1: 資源相關的系統配置發生改變致使 Activity 被殺死並從新建立。
能夠從圖中看出當 Activity 發生意外的狀況的時候,這裏的意外指的就是系統配置發生改變,Activity 會被銷燬,其onPause
,OnStop
,onDestory
函數均會被調用,同時因爲Actiivty
是在異常狀況下終止的,系統會調用onSaveInstanceState
來保存當前 Activity狀態。調用 onSaveInstanceState
的時機總會發生在 onStop
以前,至於會不會調用時機發生在 onPause
方法以前,那就說不定了,這個沒有固定的順序可言,正常狀況下通常onSaveInstanceState
不會被調用。當 Activity 被從新建立後,系統會調用onRestoreInstanceState
,而且把 Actiivty
銷燬時 onSaveInstanceState
方法所保存的Bundle 對象做爲參數傳遞給 onRestoreInstanceState
和 onCreate
方法。因此咱們能夠經過 onRestoreInstanceState
和 onCreate
方法來判斷 Actiivty
是否被重建了,若是被重建了,那麼咱們就能夠取出以前保存的數據並恢復,從時序上來看,onRestoreInstanceState
的調用時機發生在 onStart
以後。
同時,在 onSaveInstanceState
和 onRestoreInstanceState
方法中,系統自動爲咱們作了必定的恢復工做。當 Activity 在異常狀況下須要從新建立時,系統會默認爲咱們保存當前 Activity 的視圖結構。當 Activity 在異常狀況下須要從新建立時,系統會默認爲咱們保存當前 Activity 的視圖結構,而且在 Activity 重啓後爲咱們恢復這些數據,好比:文本框中用戶輸入的數據,ListView
滾動的位置等,這些 View 相關的狀態系統都可以默認爲咱們恢復。具體針對某一個特定的 View 系統 能爲咱們恢復哪些數據,咱們能夠查看 View 的源碼。和 Activity 同樣,每一個 View 都有 onSaveInstanceState
和onRestoreInstanceState
這兩個方法,看一下它們的具體實現,就能知道系統可以自動爲每一個 View 恢復哪些數據
關於保存和恢復 View 層次結構,系統的工做流程是這樣的:
首先 Activity 被意外終止時,Activity 會調用 onSaveInstanceState
去保存數據,而後Activity 會委託 Window 去保存數據,接着 Window 在委託它上面的頂級容器去保存數據。頂級容器是一個 ViewGroup
,通常來講它極可能是 DecorView
。最後頂層容器再去一一通知它的子元素來保存數據,這樣整個數據保存過程就完成了。能夠發現,這是一個典型的委託思想,上層委託下層,父容器去委託子元素去處理一件事情,這種思想在Android 中有不少應用,好比:View 的繪製過程,事件分發等都是採用相似的思想。至於數據恢復過程也是相似的,這樣就再也不重複介紹了。
狀況 2: 資源內存不足致使低優先級的 Activity 被殺死。
首先,Activity 有優先級?你確定懷疑,代碼中都沒設置過啊!優先級從何而來,其實這裏的 Activity 的優先級是指一個 Activity 對於用戶的重要程度,好比:正在與用戶進行交互的 Activity 那確定是最重要的。咱們能夠按照重要程度將 Activity 分爲如下等級:
優先級最高: 與用戶正在進行交互的 Activity,即前臺 Activity。
優先級中等: 可見但非前臺的 Activity,好比:一個彈出對話框的 Activity,可見可是非前臺運行。
優先級最低: 徹底存在與後臺的 Activity,好比:執行了onStop
。
當內存嚴重不足時,系統就會按照上述優先級去 kill 掉目前 Activity 所在的進程,並在後續經過 onSaveInstanceState
和 onRestoreInstanceState
來存儲和恢復數據。若是一個進程中沒有四大組件的執行,那麼這個進程將很快被系統殺死,所以,一些後臺工做不適合脫離四大組件獨立運行在後臺中,這樣進程更容易被殺死。比較好的方法就是將後臺工做放入 Service 中從而保證進程有必定的優先級,這樣就不會輕易地被系統殺死
總結:
上面分析了系統的數據存儲和恢復機制,咱們知道,當系統配置發生改變以後,Activity會被從新建立,那麼有沒有辦法不從新建立呢?答案是有的,接下來咱們就來分析這個問題。系統配置中有不少內容,若是某項內容發生了該變後,咱們不想系統從新建立Activity 能夠給 Activity 指定 configChanges
屬性。好比咱們不想讓 Actiivty
在屏幕旋轉的時候從新建立,就能夠給configChanges
屬性添加一些值,請繼續往下看。
與橫豎屏生命週期函數有關調用的屬性是"android:configChanges"
,關於它的屬性值設置影響以下:
orientation
:消除橫豎屏的影響keyboardHidden
:消除鍵盤的影響 screenSize
:消除屏幕大小的影響當咱們設置 Activity 的 android:configChanges
屬性爲 orientation
或者orientation
|keyboardHidden
或者不設置這個屬性的時候,它的生命週期會走以下流程:
1.剛剛啓動 Activity 的時候:
- onCreate
- onStart
- onResume
- 由豎屏切換到橫屏:
- onPause
- onSaveInstanceState //這裏能夠用來橫豎屏切換的保存數據
- onStop
- onDestroy
- onCreate
- onStart
- onRestoreInstanceState//這裏能夠用來橫豎屏切換的恢復數據
- onResume
- 橫屏切換到豎屏:
- onPause
- onSaveInstanceState
- onStop
- onDestroy
- onCreate
- onStart
- onRestoreInstanceState
- onResume
當咱們設置 Activity 的 android:configChanges
屬性爲 orientation
|screenSize
或者orientation
|screenSize
|keyboardHidden
- 剛剛啓動 Activity 的時候:
- onCreate
- onStart
- onResume
- 由豎屏切換到橫屏:
- 什麼也沒有調用
- 橫屏切換到豎屏:
- 什麼也沒有調用
並且須要注意一點的是設置了 orientation
|screenSize
屬性以後,在進行橫豎屏切換的時候調用的方法是 onConfigurationChanged()
,而不會回調 Activity 的各個生命週期函數;
固然在顯示中咱們能夠屏蔽掉橫豎屏的切換操做,這樣就不會出現切換的過程當中 Activity生命週期從新加載的狀況了,具體作法是,在 Activity 中加入以下語句:
android:screenOrientation="portrait"
始終以豎屏顯示
android:screenOrientation="landscape"
始終以橫屏顯示
若是不想設置整個軟件屏蔽橫豎屏切換,只想設置屏蔽某個 Activity 的橫豎屏切換功能的話,只須要下面操做:
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
;以豎屏顯示
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
;以橫屏顯示
最後提一點,當你橫豎屏切換的時候,若是走了銷燬 Activity 的流程,那麼須要保存當前和恢復當前 Activity 的狀態的話,咱們能夠靈活運用 onSaveInstanceState()
方法和onRestoreInstanceState()
方法。
關於這個特殊狀況,筆者在上面的生命週期圖解析的時候,貼了一個連接,這裏主要是檢驗你是否會了這個問題的答案,這裏筆者就不貼答案了,答案全在那個連接裏,你會了嗎?
當用戶後臺強殺應用程序時,當前返回棧僅有一個 activity 實例時,這時候,強殺,是會執行 onDestroy
方法的;當返回棧裏面存在多個 Activity 實例時,棧裏面的第一個沒有銷燬的 activity 執行會 ondestroy
方法,其餘的不會執行;好比說:從 mainactivity
跳轉到activity-A(或者繼續從 activity-A 再跳轉到 activity-B),這時候,從後臺強殺,只會執行 mainactivity
的 onDestroy
方法,activity-A(以及 activity-B)的 onDestroy
方法都不會執行;
前臺>可見>服務>後臺>空
前臺: 與當前用戶正在交互的 Activity 所在的進程。
可見: Activity 可見可是沒有在前臺所在的進程。
服務: Activity 在後臺開啓了 Service 服務所在的進程。
後臺: Activity 徹底處於後臺所在的進程。
空: 沒有任何 Activity 存在的進程,優先級也是最低的。
任務棧與 Activity 的啓動模式密不可分,它是用來存儲 Activity 實例的一種數據結構,Activity 的跳轉以及回跳都與這個任務棧有關。詳情請看下面的 Activity 的啓動模式。
Activity 的啓動模式,你在初學期間必定很熟悉了吧!無論你是否熟悉仍是不熟悉,跟隨筆者的思路把 Activity 的啓動模式整理一遍:
問題 1: Activity 爲何須要啓動模式?
問題 2: Activity 的啓動模式有哪些?特性如何
問題 3: 如何給 Activity 選擇合適的啓動模式
問題 1:Activity 爲何須要啓動模式?
咱們都知道啓動一個 Activity 後,這個 Activity 實例就會被放入任務棧中,當點擊返回鍵的時候,位於任務棧頂層的 Activity 就會被清理出去,當任務棧中不存在任何 Activity 實例後,系統就回去回收這個任務棧,也就是程序退出了。這只是對任務棧的基本認識,深刻學習,筆者會在以後文章中提到。那麼問題來了,既然每次啓動一個 Activity 就會把對應的要啓動的 Activity 的實例放入任務棧中,假如這個 Activity 會被頻繁啓動,那豈不是會生成不少這個 Activity 的實例嗎?對內存而言這可不是什麼好事,明明能夠一個Activity 實例就能夠應付全部的啓動需求,爲何要頻繁生成新的 Activity 實例呢?杜絕這種內存的浪費行爲,因此 Activity 的啓動模式就被創造出來去解決上面所描述的問題。
問題 2:Activity 的啓動模式有哪些?特性如何
Activity 的啓動模式有 4 種,分別是:standard,singleTop,singleTask 和singleInstance。
下面一一做介紹:
1.系統默認的啓動模式:Standard
標準模式,這也是系統的默認模式。每次啓動一個 Activity 都會從新建立一個新的實例,無論這個實例是否存在。被建立的實例的生命週期符合典型狀況下的 Activity 的生命週期。在這種模式下,誰啓動了這個 Activity,那麼這個 Activity 就運行在啓動它的那個Activity 的任務棧中。好比 Activity A 啓動了 Activity B(B 是標準模式),那麼 B 就會進入到 A 所在的任務棧中。有個注意的地方就是當咱們用 ApplicationContext
去啓動standard 模式的 Activity 就會報錯,這是由於 standard 模式的 Actiivty
默認會進入啓動它的 Activity 所屬的任務棧中,可是因爲非 Activity 類型的 Context(ApplicationContext)
並無所謂的任務棧,因此這就會出現錯誤。解決這個問題的方法就是爲待啓動的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 標記位,這樣啓動的時候就會爲它建立一個新的任務棧,這個時候啓動 Activity 實際上以singleTask 模式啓動的,讀者能夠本身仔細體會。
2.棧頂複用模式:SingleTop
在這種模式下,若是新的 Activity 已經位於任務棧的棧頂,那麼此 Activity 不會被從新建立,同時它的 onNewIntent
方法被回調,經過此方法的參數咱們能夠取出當前請求的信息。須要注意的是,這個 Activity 的 onCreate
,onStart
不會被系統調用,由於它並無發生改變。若是新的 Activity 已經存在但不是位於棧頂,那麼新的 Activity 仍然會從新重建。舉個例子,假設目前棧內的狀況爲 ABCD,其中 ABCD 爲四個 Activity,A 位於棧低,D 位於棧頂,這個時候假設要再次啓動 D,若是 D 的啓動模式爲 singleTop
,那麼棧內的狀況依然爲 ABCD;若是 D 的啓動模式爲 standard,那麼因爲 D 被從新建立,致使棧內的狀況爲 ABCDD。
3.棧內複用模式:SingTask
這是一種單例實例模式,在這種模式下,只要 Activity 在一個棧中存在,那麼屢次啓動此Activity 都不會從新建立實例,和 singleTop
同樣,系統也會回調其 onNewIntent
。具體一點,當一個具備 singleTask
模式的 Activity 請求啓動後,好比 Activity A,系統首先尋找任務棧中是否已存在 Activity A 的實例,若是已經存在,那麼系統就會把 A 調到棧頂並調用它的 onNewIntent
方法,若是 Activity A 實例不存在,就建立 A 的實例並把 A 壓入棧中。舉幾個栗子:
onNewIntent
方法,同時因爲 singleTask
默認具備clearTop
的效果,會致使棧內全部在 D 上面的 Activity所有出棧,因而最終 S1 中的狀況爲 AD。經過以上 3 個例子,你應該能比較清晰地理解 singleTask 的含義了。
4.單實例模式:SingleInstance
這是一種增強的 singleTask
模式,它除了具備 singleTask
模式全部的特性外,還增強了一點,那就是具備此種模式的 Activity 只能單獨位於一個任務棧中,換句話說,好比Activity A 是 singleInstance
模式,當 A 啓動後,系統會爲它建立一個新的任務棧,而後A 獨自在這個新的任務棧中,因爲棧內複用的特性,後續的請求均不會建立新的 Activity,除非這個獨特的任務棧被系統銷燬了。
對於 SingleInstance
,面試時你有說明它的如下幾個特色:
(1) 以 singleInstance
模式啓動的 Activity 具備全局惟一性,即整個系統中只會存在一個這樣的實例。
(2) 以 singleInstance
模式啓動的 Activity 在整個系統中是單例的,若是在啓動這樣的Activiyt
時,已經存在了一個實例,那麼會把它所在的任務調度到前臺,重用這個實例。
(3) 以 singleInstance
模式啓動的 Activity 具備獨佔性,即它會獨自佔用一個任務,被他開啓的任何 activity 都會運行在其餘任務中。
(4) 被 singleInstance
模式的 Activity 開啓的其餘 activity,可以在新的任務中啓動,但不必定開啓新的任務,也可能在已有的一個任務中開啓。換句話說,其實 SingleInstance
就是咱們剛纔分析的 SingleTask
中,分享 Activity 爲棧底元素的狀況。
總結
上面介紹了 4 種啓動模式,這裏須要指出一種狀況,咱們假設目前有 2 個任務棧,前臺任務棧的狀況爲 AB,然後臺任務棧的狀況爲 CD,這裏假設 CD 的啓動模式均爲singleTask
。如今請求啓動 D,那麼整個後臺任務棧都會被切換到前臺,這個時候整個後退列表變成了 ABCD。當用戶按 back 鍵的時候,列表中的 Activity 會一一出棧,以下圖 1所示:
注意:
前臺任務棧: 就是指和用戶正在交互的應用程序所在的任務棧。
後臺任務棧: 就是指處於後臺的應用程序所在的任務棧。
若是不是請求的 D 而是請求的 C,那麼狀況就不同了,以下圖 2 所示:
如何指定活動的啓動模式呢?在 AndroidManifest.xml 文件當註冊活動的代碼中去指定
好比: 我要把 MainActivity
活動的啓動模式指定爲 singleInstance
模式
<activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singlelnstance"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
也能夠在代碼中指定:
Intent pack = new Inten(MCPersonalCenterActivity.this,MCGiftsCenterActivity.class); pack.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(pack);
Android 中的 scheme 是一種頁面內跳轉協議,經過自定義 scheme 協議,能夠很是方便的跳轉到 app 中的各個頁面,經過 scheme 協議,服務器能夠定製化告訴 app 跳轉到哪一個頁面,能夠經過通知欄消息定製化跳轉頁面,能夠經過 H5 頁面跳轉到相應頁面等等。
查看完整的PDF版
(更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
能夠點擊關於我聯繫我獲取完整PDF
(VX:mm14525201314)