Android全面解析之Activity生命週期

前言

很高興碰見你~ 歡迎閱讀個人文章。java

關於Activity生命週期的文章,網絡上真的不少,有不少的博客也都講得至關不錯,可見Activity的重要性是很是高的。事實上,我猜想每一個android開發者接觸的第一個android組件都是Activity。咱們重新建第一個Activity開始,運行了代碼,看到模擬機上顯示了一個MainActivity標題和一行HolleWorld,今後打開Android世界的大門。android

本篇文章講解的重點是Activity的生命週期,在文章的最後也會涉及Activity的設計。不一樣於其餘的博客設計,文章採用系統化的講解,關於Activity生命週期的相關知識基本都會涉及到。git

  • 文章第一部分講解關於Activity狀態的認知;
  • 第二部分全面講解activity生命週期回調方法;
  • 第三部分是分析不一樣情景下的生命週期回調順序:
  • 第四部分是源碼分析;
  • 最後一部分是從更高的角度來思考activity以及生命週期。

那麼,咱們開始吧。數據庫

生命狀態概述

Activity是一個很重要、很複雜的組件,他的啓動不像咱們平時直接new一個對象就完事了,他須要經歷一系列的初始化。例如"剛建立狀態",「後臺狀態」,「可見狀態」等等。當咱們在界面之間進行切換的時候,activity也會在多種狀態之間進行切換,例如可見或者不可見狀態、前臺或者後臺狀態。當Activity在不一樣的狀態之間切換時,會回調不一樣的生命週期方法。咱們能夠重寫這一些方法,當進入不一樣的狀態的時候,執行對應的邏輯設計模式

在ActivityLifecycleItem`抽象類中定義了9種狀態。這個抽象類有不少的子類,是AMS管理Activity生命週期的事務類。(其實就像一個聖旨,AMS丟給應用程序,那麼應用程序就必須執行這個聖旨)Activity主要使用其中6個(這裏的6個是筆者在源碼中明確看到調用setState來設置狀態,其餘的三種並未看到調用setState方法來設置狀態,因此這裏主要講這6種),以下:api

// Activity剛被建立時
public static final int ON_CREATE = 1;
// 執行完轉到前臺的最後準備工做
public static final int ON_START = 2;
// 執行完即將與用戶交互的最後準備工做
// 此時該activity位於前臺
public static final int ON_RESUME = 3;
// 用戶離開,activity進入後臺
public static final int ON_PAUSE = 4;
// activity不可見
public static final int ON_STOP = 5;
// 執行完被銷燬前最後的準備工做
public static final int ON_DESTROY = 6;

狀態之間的跳轉不是隨意的,例如不能從ON_CREATE直接跳轉到ON_PAUSE狀態,狀態之間的跳轉收到AMS的管理。當Activity在這些狀態之間切換的時候,就會回調對應的生命週期。這裏的狀態看着很很差理解,筆者畫了個圖幫助理解一下:微信

這裏按照「可交互」「可見」「可存在」三個維度來區分Activity的生命狀態。可交互則爲是否能夠與用戶操做;可見則爲是否顯示在屏幕上;可存在,則爲該activity是否被系統殺死或者調用了finish方法。箭頭的上方爲進入對應狀態會調用的方法。這裏就先不展開講每一個狀態之間的切換,主要是讓讀者能夠更好地理解activity的狀態與狀態切換。網絡

注意,這裏使用的三個維度並非很是嚴謹的,是結合整體的顯示規則來進行區分的。app

在谷歌的官方文檔中對於onStart方法是這樣描述的:onStart() 調用使 Activity 對用戶可見,由於應用會爲 Activity 進入前臺並支持互動作準備。這也符合咱們上面的維度的區分。而當activity進入ON_PAUSE狀態的時候,Activity是可能依舊可見的,可是不可交互。如操做另外一個應用的懸浮窗口的時候,當前應用的activity會進入ON_PAUSE狀態。ide

可是!在activity啓動的流程中,直到onResume方法被調用,界面依舊是不可見的。這點在後面的源碼分析再詳細解釋。因此這裏的狀態區分維度,僅僅只是整體上的一種區分,能夠這麼認爲,但細節上並非很是嚴謹的。須要讀者注意一下。

生命週期的一個重要做用就是讓activity在不一樣狀態之間切換的時候,能夠執行對應的邏輯。舉個栗子。咱們在界面A使用了相機資源,當咱們切換到下個界面B的時候,那麼界面A就必須釋放相機資源,這樣纔不會致使界面B沒法使用相機;而當咱們切回界面A的時候,又但願界面A繼續保持擁有相機資源的狀態;那麼咱們就須要在界面不可見的時候釋放相機資源,而在界面恢復的時候再次獲取相機資源。每一個Activity通常狀況下能夠認爲是一個界面或者說,一個屏幕。當咱們在界面之間進行導航切換的時候,其實就是在切換Activity。當界面在不一樣狀態之間進行切換的時候,也就是Activity狀態的切換,就會回調activity相關的方法。例如當界面不可見的時候會回調onStop方法,恢復的時候會回調onReStart方法等。

在合適的生命週期作合適的工做會讓app變得更加有魯棒性。避免當用戶跳轉別的app的時候發生崩潰、內存泄露、當用戶切回來的時候失去進度、當用戶旋轉屏幕的時候失去進度或者崩潰等等。這些都須要咱們對生命週期有必定的認知,才能在具體的場景下作出最正確的選擇。

這一部分概述並無展開講生命週期,而是須要重點理解狀態與狀態之間的切換,生命週期的回調就發生在不一樣的狀態之間的切換。咱們學習生命週期的一個重要目的就是可以在對應的業務場景下作合適的工做,例如資源的申請、釋放、存儲、恢復,讓app更加具備魯棒性。

重要生命週期解析

關於Activity重要的生命週期回調方法谷歌官方有了一張很是重要的流程圖,能夠說是人人皆知。我在這張圖上加上了一些經常使用的回調方法。這些方法嚴格上並不算Activity的生命週期,由於並無涉及到狀態的切換,但卻在Activity的整個生命歷程中發揮了很是大的做用,也是很重要的回調方法。方法不少,咱們先看圖,再一個個地解釋。看具體方法解釋的時候必定要結合下面這張圖以及上一部分概述的圖一塊兒理解。

主要生命週期

首先咱們先看到最重要的七個生命週期,這七個生命週期是嚴格意義上的生命週期,他符合狀態切換這個關鍵定義。這部份內容建議結合概述部分的圖一塊兒理解。(onRestart並不涉及狀態變換,但由於執行完他以後會立刻執行onStart,因此也放在一塊兒講)

  • onCreate:當Activity建立實例完成,並調用attach方法賦值PhoneWindow、ContextImpl等屬性以後,調用此方法。該方法在整個Activity生命週期內只會調用一次。調用該方法後Activity進入ON_CREATE狀態。

    該方法是咱們使用最頻繁的一個回調方法。

    咱們須要在這個方法中初始化基礎組件和視圖。如viewmodel,textview。同時必須在該方法中調用setContentView來給activity設置佈局。

    這個方法接收一個參數,該參數保留以前狀態的數據。若是是第一次啓動,則該參數爲空。該參數來自onSaveInstanceState存儲的數據。只有當activity暫時銷燬而且預期必定會被從新建立的時候纔會被回調,如屏幕旋轉、後臺應用被銷燬等

  • onStart:當Activity準備進入前臺時會調用此方法。調用後Activity會進入ON_START狀態。

    要注意理解這裏的前臺的意思。雖然谷歌文檔中表示調用該方法以後activity可見,以下圖:

    可是咱們前面講到,前臺並不意味着Activity可見,只是表示activity處於活躍狀態。這也是谷歌文檔裏讓我比較迷惑的地方之一。(事實上谷歌文檔有挺多地方寫的缺少嚴謹,可能考慮到易懂性,就犧牲了一點嚴謹性吧)。

    前臺activity通常只有一個,因此這也意味着其餘的activity都進入後臺了。這裏的先後臺須要結合activity返回棧來理解,後續筆者再寫一篇關於返回棧的。

    這個方法通常用於從別的activity切回來本activity的時候調用。

  • onResume:當Activity準備與用戶交互的時候調用。調用以後Activity進入ON_RESUME狀態。

    注意,這個方法一直被認爲是activity必定可見,且準備好與用戶交互的狀態。但事實並不一直是這樣。若是在onReume方法中彈出popupWindow你會收穫一個異常:token is null,表示界面尚沒有被添加到屏幕上。

    可是,這種狀況只出如今第一次啓動activity的時候。當activity啓動後decorview就已經擁有token了,再次在onReume方法中彈出popupWindow就不會出現問題了。

    所以,在onResume調用的時候activity是否可見要區分是不是第一次建立activity

    onStart方法是後臺與前臺的區分,而這個方法是是否可交互的區分。使用場景最可能是在當彈出別的activity的窗口時,原activity就會進入ON_PAUSE狀態,可是仍然可見;當再次回到原activity的時候,就會回調onResume方法了。

  • onPause:當前activity窗口失去焦點的時候,會調用此方法。調用後activity進入ON_PAUSE狀態,並進入後臺。

    這個方法通常在另外一個activity要進入前臺前被調用。只有當前activity進入後臺,其餘的activity才能進入前臺。因此,該方法不能作重量級的操做,否則則會引用界面切換卡頓

    通常的使用場景爲界面進入後臺時的輕量級資源釋放。

    最好理解這個狀態就是彈出另外一個activity的窗口的時候。由於前臺activity只能有一個,因此當前可交互的activity變成另外一個activity後,原activity就必須調用onPause方法進入ON_PAUSE狀態;可是!!仍然是可見的,只是沒法進行交互。這裏也能夠更好地體會前臺可交互與可見性的區別。

  • onStop:當activity不可見的時候進行調用。調用後activity進入ON_STOP狀態。

    這裏的不可見是嚴謹意義上的不可見。

    當activity不可交互時會回調onPause方法並進入ON_PAUSE狀態。但若是進入的是另外一個全屏的activity而不是小窗口,那麼當新的activity界面顯示出來的時候,原Activity纔會進入ON_STOP狀態,並回調onStop方法。同時,activity第一建立的時候,界面是在onResume方法以後才顯示出來,因此onStop方法會在新activity的onResume方法回調以後再被回調。

    注意,被啓動的activity並不會等待onStop執行完畢以後再顯示。於是若是onStop方法裏作一些比較耗時的操做也不會致使被啓動的activity啓動延遲。

    onStop方法的目的就是作資源釋放操做。由於是在另外一個activity顯示以後再被回調,因此這裏能夠作一些相對重量級的資源釋放操做,如中斷網絡請求、斷開數據庫鏈接、釋放相機資源等。

    若是一個應用的所有activity都處於ON_STOP狀態,那麼這個應用是頗有可能被系統殺死的。而若是一個ON_STOP狀態的activity被系統回收的話,系統會保留該activity中view的相關信息到bundle中,下一次恢復的時候,能夠在onCreate或者onRestoreInstanceState中進行恢復。

  • onRestart :當從另外一個activity切回到該activity的時候會調用。調用該方法後會當即調用onStart方法,以後activity進入ON_START狀態。

    這個方法通常在activity從ON_STOP狀態被從新啓動的時候會調用。執行該方法後會當即執行onStart方法,而後Activity進入ON_START狀態,進入前臺。

  • onDestroy:當activity被系統殺死或者調用finish方法以後,會回調該方法。調用該方法以後activity進入ON_DESTROY狀態。

    這個方法是activity在被銷燬前回調的最後一個方法。咱們須要在這個方法中釋放全部的資源,防止形成內存泄漏問題。

    回調該方法後的activity就等待被系統回收了。若是再次打開該activity須要從onCreate開始執行,從新建立activity。

那到這裏七個最關鍵的生命週期方法就講完了。須要讀者注意的是,在概述一圖中,咱們使用了三個維度來進行區分不一樣類型的狀態,可是很明顯,同一類型的狀態並非等價的。如ON_START狀態表示activity進入前臺,而ON_PAUSE狀態卻表示activity進入後臺。這可能也是爲何谷歌要區分出on_start和on_pause兩個狀態,他們表明並非一致的狀態。

這七個生命週期回調方法是最重要的七個生命週期回調方法,須要讀者好好理解每一個回調方法設計到的activity的狀態轉換。而理解了狀態轉換後,也就能夠寫出更增強壯的代碼了。

其餘生命週期回調方法

  • onActivityResult

這個方法也很常見,他須要結合startActivityForResult一塊兒使用。

使用的場景是:啓動一個activity,並指望在該activity結束的時候返回數據。

當啓動的activity結束的時候,返回原activity,原activity就會回調onActivityResult方法了。該方法執行在其餘全部的生命週期方法前。關於onActivityResult如何使用這裏就不展開了,咱們主要介紹生命週期。

  • onSaveInstanceState/onRestoreInstanceState

這兩個方法,主要用於在Activity被意外殺死的狀況下進行界面數據存儲與恢復。什麼叫意外殺死呢?

若是你主動點擊返回鍵、調用finish方法、從多任務列表清除後臺應用等等,這些操做表示用戶想要完整得退出activity,那麼就沒有必要保留界面數據了,因此也不會調用這兩個方法。而當應用被系統意外殺死,或者系統配置更改致使的activity銷燬,這個時候當用戶返回activity時,指望界面的數據還在,則會經過回調onSaveInstanceState方法來保存界面數據,而在activity從新建立並運行的時候調用onRestoreInstanceState方法來恢復數據。事實上,onRestoreInstanceState方法的參數和onCreate方法的參數是一致的,只是他們兩個方法回調的時機不一樣。所以,判斷是否執行的關鍵因素就是用戶是否指望返回該activity時界面數據仍然存在

這裏須要注意幾個點:

  1. 不一樣android版本下,onSaveInstanceState方法的調用時機是不一樣的。目前筆者的源碼是api30,在官方註釋中能夠看到這麼一句話:

    /*If called, this method will occur after {@link #onStop} for applications
     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
     * For applications targeting earlier platform versions this method will occur
     * before {@link #onStop} and there are no guarantees about whether it will
     * occur before or after {@link #onPause}.
     */

    翻譯過來意思就是,在api28及以上版本onSaveInstanceState是在onStop以後調用的,可是在低版本中,他是在onStop以前被調用的,且與onPause之間的順序是不肯定的。

  2. 當activity進入後臺的時候,onSaveInstanceState方法則會被調用,而不是異常狀況下才會調用onSaveInstanceState方法,由於並不肯定在後臺時,activity是否會被系統殺死,因此以最保險的方法,先保存數據。當確實是由於異常狀況被殺死時,返回activity用戶指望界面須要恢復數據,纔會調用onRestoreInstanceState來恢復數據。可是,activity直接按返回鍵或者調用finish方法直接結束Activity的時候,是不會回調onSaveInstanceState方法,由於很是明確下一次返回該activity用戶指望的是一個乾淨界面的新activity。

  3. onSaveInstanceState不能作重量級的數據存儲。onSaveInstanceState存儲數據的原理是把數據序列化到磁盤中,若是存儲的數據過於龐大,會致使界面卡頓,掉幀等狀況出現。

  4. 正常狀況下,每一個view都會重寫這兩個方法,當activity的這兩個方法被調用的時候,會向上委託window去調用頂層viewGroup的這兩個方法;而viewGroup會遞歸調用子view的onSaveInstanceState/onRestoreInstanceState方法,這樣全部的view狀態就都被恢復了。

關於界面數據恢復這裏也不展開細講了,有興趣的讀者能夠自行深刻研究。

  • onPostCreate

這個方法其實和onPostResume是同樣的,一樣的還有onContextChange方法。這三個方法都是不經常使用的,這裏也點出其中一個來統一講一下。

onPostCreate方法發生在onRestoreInstanceState以後,onResume以前,他表明着界面數據已經徹底恢復,就差顯示出來與用戶交互了。在onStart方法被調用時這些操做還沒有完成。

onPostResume是在Resume方法被徹底執行以後的回調。

onContentChange是在setContentView以後的回調。

這些方法都不經常使用,僅作了解。若是真的遇到了具體的業務需求,也能夠拿出來用一下。

  • onNewIntent

這個方法涉及到的場景也是重複啓動,可是與onRestart方法被調用的場景是不一樣的。

咱們知道activity是有多種啓動模式的,其中singleInstance、singleTop、singleTask都保證了在必定狀況下的單例狀態。如singleTop,若是咱們啓動一個正處於棧頂且啓動模式爲singleTop的activity,那麼他並不會在建立一個activity實例,而是會回調該activity的onNewIntent方法。該方法接收一個intent參數,該參數就是新的啓動Intent實例。

其餘的狀況如singleTask、singleInstance,當遇到這種強制單例狀況時,都會回調onNewIntent方法。關於啓動模式這裏也不展開,後續筆者可能會再出一期文章講啓動模式。

場景生命週期流程

這一部分主要是講解在一些場景下,生命週期方法的回調順序。對於當個Activity而言,上述流程圖已經展現了各類狀況下的生命週期回調順序了。可是,當啓動另外一個activity的時候,究竟是onStop先執行,仍是被啓動的onStart先執行呢?這些就變得比較難以肯定。

驗證生命週期回調順序最好的方法就是寫demo,經過日誌打印,能夠很明顯地觀察到生命週期的回調順序。固然,查看源碼也是一個不錯的方法,可是須要對系統源碼有必定的認識,咱們仍是選擇簡單粗暴的方法。

正常啓動與結束

onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy

這種狀況的生命週期比較好理解,就是常規的啓動與結束,也不會涉及到第二個activity。最後看日誌打印:

Activity切換

Activity1:onPause
Activity2:onCreate -> onStart -> onResume
Activity1:onStop

當切換到另外一個activity的時候,本activity會先調用onPause方法,進入後臺;被啓動的activity依次調用三個回調方法後準備與用戶交互;這時原activity再調用onStop方法變得不可見,最後被啓動的activity纔會顯示出來。

理解這個生命週期順序只須要記住兩個點:先後臺、是否可見。onPause調用以後,activity會進入後臺。而前臺交互的activity只能有一個,因此原activity必須先進入後臺後,目標activity才能啓動並進入前臺。onStop調用以後activity變得不可見,於是只有在目標activity即將要與用戶交互的時候,須要進行顯示了,原Activity纔會調用onStop方法進入不可見狀態。

當從Activity2回退到Activity1的時候,流程也是相似的,只是Activity1會在其餘生命週期以前執行一次onRestart,跟前面的流程是相似的。讀者能夠看一下下面的日誌打印,這裏就再也不贅述了。

下面看一下切換到另外一個activity的生命週期日誌打印:

這裏咱們看到最後回調了onSaveInstanceState方法,前面咱們講到了,當activity進入後臺的時候,會調用該方法來保存數據。由於並不知道在後臺時activity是否會被系統殺死。下面再看一下從activity2返回的時候,生命週期的日誌打印:

屏幕旋轉

running -> onPause -> onStop -> onSaveInstanceState -> onDestroy

onCreate -> onStart -> onRestoreInstanceState -> onResume

當因資源配置改變時,activity會銷燬重建,最多見的就是屏幕旋轉。這個時候屬於異常狀況的Activity生命結束。於是,在銷燬的時候,會調用onSaveInstanceState來保存數據,在從新建立新的activity的時候,會調用onRestoreInstanceState來恢復數據。

看一下日誌打印:

後臺應用被系統殺死

onDestroy

onCreate -> onStart -> onRestoreInstanceState -> onResume

這個流程跟上面的資源配置更改是很像的,只是每一個activity不可見的時候,會回調onSaveInstanceState提早保存數據,那麼在被後臺殺死的時候,就不須要再次保存數據了。

具備返回值的啓動

onActivityResult -> onRestart -> onResume

這裏主要針對使用startActivityForResult方法啓動另外一個activity,當該activity銷燬並返回時,原activity的onActivityResult方法的執行時機。大部分流程和activity切換是同樣的。但在返回原Activity時,onActivityResult方法會在其餘全部的生命週期方法執行前被執行。看一下日誌打印:

重複啓動

onPause -> onNewIntent -> onResume

這個流程是比較容易在學習生命週期的時候被忽略的。前面已經有講到了關於onNewIntent的相關介紹,這裏就再也不贅述。主要是記得若是當前activity正處於棧頂,那麼會先回調onPause以後再回調onNewIntent。關於啓動模式以及返回棧的設計這裏就不展開講了,記住生命週期就能夠了。看一下日誌打印:

從源碼看生命週期

到這裏關於生命週期的一些應用知識就已經講得差很少了,這一部分是深刻源碼,去探究生命週期在源碼中是如何實現的。這樣對生命週期會有更加深入的理解,同時能夠更加了解android系統的源碼設計。

因爲生命週期方法不少,筆者不可能一一講解,這樣篇幅就太大了且沒有意義。這一部分的內容一共分爲兩個部分:第一部分是概述一下ActivityThread中關於每一個生命週期的調用方法,這樣你們就懂得如何去尋找對應的源碼來研究;第二部分是拿onResume這個方法來舉例講解,同時解釋爲何在第一次啓動時,當onResume被調用時界面依然不可見。

從ActivityThread看生命週期

咱們都知道,Activity的啓動是受AMS調配的,那具體的調配方式是如何的呢?

經過Handler機制一文咱們知道,android的程序執行是使用handler機制來實現消息驅動型的。AMS想要控制Activity的生命週期,就必須不斷地向主線程發送message;而程序想要執行AMS的命令,就必須handle這些message執行邏輯,兩端配合,才能達到這種效率。

打個比方,領導要吩咐下屬去工做,他確定不會把工做的具體流程都給下屬,而只是會發個命令,如:給明天的演講作個ppt,給我預定個下星期的飛機等等。那麼下屬,就必須根據這些命令來執行指定的邏輯。因此,在android程序,確定有一系列的邏輯,來分別執行來自AMS的「命令」。這就是ActivityThread中的一系列handlexxx方法。給個我在vs code中的搜索圖感覺一下:

固然,應用程序不止收到AMS的管理,一樣的還有WMS、PMS等等系統服務。系統服務是運行在系統服務進程的,當系統服務須要控制應用程序的時候,會經過Binder跨進程通訊把消息發送給應用程序。應用程序的Binder線程會經過handler把消息發送給主線程去執行。於是,從這裏也能夠看出,當應用程序剛被建立的時候,必須初始化的有主線程、binder線程、主線程handler、以及提早編寫了命令的執行邏輯的類ActivityThread。光說不畫假解釋,畫個圖感覺一下:

回到咱們的生命週期主題。關於生命週期命令的執行方法主要有:

handleLaunchActivity;
handleStartActivity;
handleResumeActivity;
handlePauseActivity;
handleStopActivity;
handleDestroyActivity;

具體的方法固然不止這麼多,只是列出一些比較經常使用的。這些方法都在ActivityThread中。ActivityThread每一個應用程序有且只有一個,他是系統服務「命令」的執行者。

瞭解了AMS如何調配以後,那麼他們的執行順序如何肯定呢?AMS是先發送handleStartActivity命令呢,仍是先發送handleResumeActivity?這裏就須要咱們對Activity的啓動流程有必定的認知,感興趣讀者能夠點擊Activity啓動流程前往學習,這裏就不展開了。

最後再延伸一下,那,ActivityThread可不能夠本身決定執行邏輯,而不理會AMS的命令呢?答案確定是no。你想啊,若是在公司裏,你沒有老闆的贊成 ,你能動用公司的資源嗎?回到Android系統也是同樣的,沒有AMS的受權,應用程序是沒法獲得系統資源的,因此AMS就保證了每個程序都必須符合必定的規範。關於這方面的設計,讀者感興趣能夠閱讀context機制這篇文章瞭解一下,從新認識一下context。

好了,扯得有點遠,咱們回到主題。下面呢就不展開去跟蹤整個流程了,而是定位到具體的handle方法去看看具體執行了什麼邏輯,對源碼流程感性去的讀者能夠自行研究,限於篇幅這裏就不展開了。下面主要介紹handleResumeActivity方法。

解析onRusume源碼

根據咱們前面的學習,handleResumeActivity確定是在handleLaunchActivityhandleStartActivity以後被執行的。咱們直接來看源碼:

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ...
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ...;
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        ...
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    }
    ...
}

代碼我截取了兩個很是重要的部分。performResumeActivity最終會執行onResume方法;activity.makeVisible();是真正讓界面顯示在屏幕個上的方法,咱們看一下makeVisible():

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

若是還沒有添加到屏幕上,那麼會調用windowManager的addView方法來添加,以後,activity界面才真正顯示在屏幕上。迴應以前的問題:爲何在onResume彈出popupWindow會拋異常而彈出dialog卻不會?緣由就是這個時候activity的界面還沒有添加到屏幕上,而popupWindow須要依附於父界面,這個時候彈出就會拋出token is null異常了。而Dialog屬於應用層級窗口,不須要依附於任何窗口,因此dialog在onCreate方法中彈出都是沒有問題的。爲了驗證咱們的判斷,我在生命週期中打印decorView的windowToken,當decorView被添加到屏幕上後,就會被賦值token了,看日誌打印:

能夠看到,直到onPostResume方法執行,界面依舊沒有顯示在屏幕上。而直到onWindowFocusChange被執行時,界面纔是真正顯示在屏幕上了。

好了,讓咱們再回到一開始的源碼,深刻performResumeActivity方法中看看,在哪裏執行了onResume方法:

public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
        String reason) {
    ...
    try {
        ...
        if (r.pendingIntents != null) {
            // 判斷是否須要執行newIntent方法
            deliverNewIntents(r, r.pendingIntents);
            r.pendingIntents = null;
        }
        if (r.pendingResults != null) {
            // 判斷是否須要執行onActivityResult方法
            deliverResults(r, r.pendingResults, reason);
            r.pendingResults = null;
        }
        // 回調onResume方法
        r.activity.performResume(r.startsNotResumed, reason);

        r.state = null;
        r.persistentState = null;
        // 設置狀態
        r.setState(ON_RESUME);

        reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");
    } 
    ...
}

這個方法的重點就是,先判斷是不是須要執行onNewIntent或者onActivityResult的場景,若是沒有則執行調用performResume方法,咱們深刻performResume看一下:

final void performResume(boolean followedByPause, String reason) {
    dispatchActivityPreResumed();
    performRestart(true /* start */, reason);
	...
    mInstrumentation.callActivityOnResume(this);
    ...
    onPostResume();
   	...
}

public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();
    ...
}

一樣只看重點。首先會調用performRestart方法,這個方法內部會判斷是否須要執行onRestart方法和onStart方法,若是是從別的activity返回這裏是確定要執行的。而後使用Instrumentation來回調Activity的onResume方法。當onResume回調完成後,會再調用onPostResume()方法。

那麼到這裏關於handleResumeActivity的方法就講完了,爲何在onResume甚至onPostResume方法被回調的時候界面還沒有顯示,也有了更加深入的認識。具體的代碼邏輯很是多,而關於生命週期的代碼我只挑了重點來說,其餘的源碼,感興趣的讀者能夠自行去查閱源碼。筆者這裏更多的是充當一個拋磚引玉的效果。要從源碼中學習到知識,就必須本身手動去閱讀源碼,跟着文章看完事實上收穫是不大的。

從系統設計看Activity與其生命週期

在筆者認爲,每個知識,都是在具體的場景下爲了解決具體的問題,經過權衡各類條件設計出來的。在學習了每個知識以後,筆者老是喜歡反過來,思考一下這一塊知識的底層設計思想是什麼,他是須要解決什麼問題,權衡了什麼條件。經過不斷思考來從一個更高的角度來看待每個知識點。

要理解生命週期的設計,首先須要理解Activity自己。想一下,若是沒有Activity,那麼咱們該如何編寫程序?有沒有突然反應到,沒有了activity,咱們的程序竟無處下手?由於這涉及到Activity的一個最大的做用:Activity 類是 Android 應用的關鍵組件,而 Activity 的啓動和組合方式則是該平臺應用模型的基本組成部分

相信不少讀者都寫過c語言、java或者其餘語言的課程設計,咱們的程序入口就是main函數。從main函數開始,根據用戶的輸入來進入不一樣的功能模塊,如更改信息模塊、查閱模塊等等。以功能模塊爲基本組成部分的應用模型是咱們最初的程序設計模型。而android程序,咱們會說這個程序有幾個界面。咱們更關注的是界面之間的跳轉,而不是功能模塊之間的跳轉。咱們在設計程序的時候,咱們會說這個界面有什麼功能,那個界面有什麼功能,多個界面之間如何協調。對於用戶來講,他們感知的也是一個個獨立的界面。當咱們經過通信軟件調用郵箱app的發送郵件界面時,咱們喜歡看到的只是發送郵件的界面,而不須要看到收件箱、登陸註冊等界面。以功能模塊爲應用模型的設計從一個主功能模塊入口,而後經過用戶的輸入去調用不一樣的功能模塊。與其相似,android程序也有一個主界面,經過這個主界面,接受用戶的操做去調用其餘的界面。組成android程序的,是一個個的界面,而每個界面,對應一個Activity。所以,Activity是android平臺應用模型的基本組成成分

功能模塊的應用模型從main方法進入主功能模塊,而android程序從ActivityThread的main方法開始,接收AMS的調度啓動「LaunchActivity」,也就是咱們在AndroidManifest中配置爲main的activity,當應用啓動的時候,就會首先打開這個activity。那麼第一個界面被打開,其餘的界面就根據用戶的操做來依次跳轉了。

那如何作到每一個界面之間彼此解耦、各自的顯示不發生混亂、界面之間的跳轉有條不紊等等?這些工做,官方都幫咱們作好了。Activity就是在這個設計思想下開發出來的。當咱們在Activity上開發的時候,就已經沿用了他的這種設計思想。當咱們開發一個app的時候,最開始要考慮的,是界面如何設計。設計好界面以後,就是考慮如何開發每一個界面了。那咱們如何自定義好每個界面?如何根據咱們的需求去設計每一個界面的功能?Activity並無main方法,咱們的代碼該寫在哪裏被執行?答案就是:生命週期回調方法

到這裏,你應該能夠理解爲何啓動activity並非一句new就能夠解決的吧?Activity承擔的責任很是多,須要初始化的邏輯也很是多。當Activity被啓動,他會根據自身的啓動狀況,來回調不一樣的生命週期方法。其中,承擔初始化整個界面已經各個功能組件的初始化任務的就是onCreate方法。他有點相似於咱們功能模塊的入口函數,在這裏咱們經過setContentView來設計咱們界面的佈局,經過setOnClickListenner來給每一個view設置監聽等等。在MVVM設計模式中,還須要初始化viewModel、綁定數據等等。這是生命週期的第一個很是重要的意義所在。

而當界面的顯示、退出,咱們須要爲之申請或者釋放資源。如我上文舉的相機例子,我在微信的掃一掃申請了相機權限,若是進入後臺的時候沒有釋放資源,那麼打開系統相機就沒法使用了,資源被佔領了。所以,生命週期的另外一個重要的做用就是:作好資源的申請與釋放,避免內存泄露

其餘生命週期的做用,如界面數據恢復、界面切換邏輯處理等等就再也不贅述了,前面已經都有涉及到。

這一部分的重點就是理解android應用程序是以Activity爲基本組成部分的應用模型這個點。當界面的啓動以及不一樣界面之間進行切換的時候,也就能夠更加感知生命週期的做用了。

最後

關於Activity生命週期的內容,在一篇文章講完整是不可能的。當對他研究地越深,涉及到內容就會越多。每一個知識點就是像是瓜藤架上的一個瓜,若是單純摘瓜,那就是一個瓜;若是抓着藤蔓往外拔,那麼整個架子都會被扯出來。這篇文章也當是拋磚引玉,在講生命週期相關的知識講完以後,提供給讀者一個思考的思路。

但願文章對你有幫助。

全文到此,原創不易,以爲有幫助能夠點贊收藏評論轉發。
筆者才疏學淺,有任何想法歡迎評論區交流指正。
如需轉載請評論區或私信交流。

另外歡迎光臨筆者的我的博客:傳送門

相關文章
相關標籤/搜索