您好,登錄后才能下訂單哦!
(^▽^)經常發現學著學著,由于學習的東西越來越多,接觸的東西越來越多,逐漸的吧自己的最基礎的東西忘得差不多了(o(╥﹏╥)o我也差不多忘了很多東西了)
發現越優秀的人 越注重細節,基礎更加扎實和鞏固分享一下自己整理的面試學習路線
請查看完整的PDF版
(更多完整項目下載。未完待續。源碼。圖文知識后續上傳github。)
可以點擊關于我聯系我獲取完整PDF
(VX:mm14525201314)
Activity 實際上只是一個與用戶交互的接口而已
?Active/Paused/Stopped/Killed
Activie: 當前 Activity 正處于運行狀態,指的是當前 Activity 獲取了焦點。
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()
詳細生命周期如下:
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)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。