Android基礎之Activity全解析

RobinHE發表於2019-04-06

概念

Android四大應用元件之一,與使用者互動的介面,介面的載體。

生命週期

Android基礎之Activity全解析

  • onCreate(),生命週期第一個方法,表示Activity正在建立過程中。一般做一些初始化工作,setContentView()載入介面佈局等。
  • onStart(),表示Activity正在啟動過程中,Activity可見但不可與使用者互動(已經顯示出來,但是看不到)。
  • onReStart(),表示Activity正在重新啟動過程中,一般Activity由不可見到可見過程中會回撥此方法。
  • onResume(),Activity顯示在前臺並可與使用者互動。
  • onPause(),表示Activity正在停止,可做停止動畫,儲存資料操作,不能做耗時操作。
  • onStop(),表示Activity即將停止,可做輕量級回收工作。
  • onDestory(),表示Activity即將銷燬,生命週期最後一個方法。

從整個生命週期來說,onCreate與onDestory是配對的,標誌銷燬與建立,真個生命週期只會回撥一次。onStart與onStop是配對的,標誌Activity是否可見。onResume與onPause是配對的,標誌是否處於前臺可與使用者互動。

  • 首次啟動Activity,系統會先回撥onCreate方法然後呼叫onStart,onResume,Activity進入執行狀態。
  • 當前Activity被其他Activity覆蓋其上或被鎖屏(可以理解為沒有完全遮擋介面),系統會呼叫onPause方法,暫停當前Activity的執行。
  • 當前Activity由被覆蓋狀態回到前臺或解鎖屏,系統會呼叫onResume方法,再次進入執行狀態。
  • 當前Activity轉到新的Activity介面或按Home鍵回到主屏,自身退居後臺,系統會先呼叫onPause方法,然後呼叫onStop方法,進入停滯狀態。(如果新Activity採用透明主題,當前Activity不會回撥onStop方法)
  • 使用者後退回到此Activity,系統會先呼叫onRestart方法,然後呼叫onStart方法,最後呼叫onResume方法,再次進入執行狀態。
  • 使用者退出當前Activity,系統先呼叫onPause方法,然後呼叫onStop方法,最後呼叫onDestory方法,結束當前Activity。

A Activity啟動B Activity的生命週期情況: A執行onCreate,onStart,onResume到可互動狀態,此時啟動B Activity,首先將執行A Activity的onpause暫停A Activity執行,然後執行B Activity的onCreate,onStart,onResume到可互動狀態,然後再執行A Activity的onStop方法(如果B Activity未完全遮擋A Activity, 將不會回撥A Activity的onStop方法),如果啟動B Activity時失敗,A Activity將從onPause狀態回到onResume執行。

home鍵/鎖屏生命週期情況: onPause -> onStop

back鍵/清除recent生命週期情況: onPause -> onStop -> onDestory

異常情況的銷燬與恢復

當系統“未經使用者許可”銷燬activity時,會回撥onSaveInstanceState方法儲存資料,讓介面可以恢復。onSaveInstanceState方法什麼時候會執行?

  1. 當使用者按下HOME鍵時。系統不知道你按下HOME後要執行多少其他的程式,自然也不知道activity是否會被銷燬,故系統會呼叫onSaveInstanceState,讓使用者有機會儲存某些非永久性的資料。
  2. 長按HOME鍵,選擇執行其他的程式時。
  3. 按下電源按鍵(關閉螢幕顯示)時。
  4. 從activity A中啟動一個新的activity時。
  5. 螢幕方向切換時,例如從豎屏切換到橫屏時。

onCreate()裡也有Bundle引數,可以用來恢復資料,它和onRestoreInstanceState有什麼區別? 因為onSaveInstanceState 不一定會被呼叫,所以onCreate()裡的Bundle引數可能為空,如果使用onCreate()來恢復資料,一定要做非空判斷。而onRestoreInstanceState的Bundle引數一定不會是空值,因為它只有在上次activity被回收了才會呼叫。而且onRestoreInstanceState是在onStart()之後被呼叫的。有時候我們需要onCreate()中做的一些初始化完成之後再恢復資料,用onRestoreInstanceState會比較方便。onSaveInstanceState呼叫時機在onStop()前,onRestoreInstanceState的呼叫時機實在onStart()之後。

注意:

onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被呼叫的,onRestoreInstanceState被呼叫的前提是,activity A“確實”被系統銷燬了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被呼叫,例如,當正在顯示activity A的時候,使用者按下HOME鍵回到主介面,然後使用者緊接著又返回到activity A,這種情況下activity A一般不會因為記憶體的原因被系統銷燬,故activity A的onRestoreInstanceState方法不會被執行。

onSaveInstanceState和onRestoreInstanceState方法中,系統自動做了一系列恢復動作,系統會預設儲存當前Activity檢視結構,並在Activity重啟後恢復這些資料。Activity被意外終止時,首先會回撥onSaveInstanceState儲存資料,然後委託Window -> DecorView -> View一層層儲存資料。與Activity一樣,每個View都有onSaveInstanceState和onRestoreInstanceState方法。

啟動模式

任務棧(task stack)

為了記錄使用者開啟了那些activity,記錄這些activity開啟的先後順序,google引入任務棧(task stack)概念,以棧的結構(先進後出)將依次開啟的activity記錄儲存在task stack中。

啟動模式

Standard

預設Standard模式,假設沒有為Activity設定啟動模式的話,預設是標準模式。即每次啟動一個Activity都會又一次建立一個新的例項入棧,無論這個例項是否存在。Standard模式的Activity預設會進入啟動它的Activity所屬的任務棧(啟動它的Activity為SingleInstance的情況除外)。

SingleTop

當建立的Activity已經處於棧頂時,此時會直接複用棧頂的Activity,不會再建立新的Activity。若須要建立的Activity不處於棧頂,此時會又一次建立一個新的Activity入棧,同Standard模式一樣。 棧頂的Activity被直接複用時,onCreate、onStart不會被系統呼叫,而是回撥onNewIntent方法(Activity被正常建立時不會回撥此方法),然後再執行onResume。(實際是onPause -> onNewIntent -> onResume)

SingleTask

棧內複用模式,若須要建立的Activity已經處於棧中時,此時不會建立新的Activity,而是將存在棧中的Activity上面的其他Activity所有銷燬,使它成為棧頂。 同SingleTop模式中一樣,onCreate、onStart不會被系統呼叫,僅僅回撥Activity中的onNewIntent方法。

比如啟動Activity A,先找其對應的任務棧,如果不存在,則先新建任務棧,在建立Activity A放到任務棧中。如果存在,則判斷棧中是否存在Activity A的例項,如果存在則把Activity A調到棧頂,然後呼叫其onNewIntent方法。如果不存在Activity A的例項,則新建Activity A放到任務棧中。

SingleTask會導致它之上的棧內所有Activity出棧

SingleInstance

具有此模式的Activity僅僅能單獨位於一個任務棧中

taskAffinity

taskAffinity,可以翻譯為任務相關性。這個引數標識了一個 Activity 所需要的任務棧的名字,預設情況下,所有 Activity 所需的任務棧的名字為應用的包名,當 Activity 設定了 taskAffinity 屬性,那麼這個 Activity 在被建立時就會執行在和 taskAffinity 名字相同的任務棧中,如果沒有,則新建 taskAffinity 指定的任務棧,並將 Activity 放入該棧中。另外,taskAffinity 屬性主要和singleTask或者allowTaskReparenting屬性配對使用,在其他情況下沒有意義。

  • taskAffinity對Standard與SingleTop,SingleInstance沒有影響
  • taskAffinity對SingleTask的影響: 以A啟動B來說 當A和B的taskAffinity相同時:第一次建立B的例項時,並不會啟動新的task,而是直接將B新增到A所在的task;當B的例項已經存在時,將B所在task中位於B之上的全部Activity都移除,B就成為棧頂元素,實現跳轉到B的功能。 當A和B的taskAffinity不同時:第一次建立B的例項時,會啟動新的task,然後將B新增到新建的task中;當B的例項引進存在,將B所在task中位於B之上的全部Activity都移除,B就成為棧頂元素(也是root Activity),實現跳轉到B的功能。

使用場景

  • Standard 普通情況下使用
  • SingleTop 適用於同型別的Activity,例如通知欄訊息接收啟動的Activity
  • SingleTask 主介面
  • SingleInstance 適合公開成通用功能的Activity,例如撥號,鬧鐘介面等

Activity Flags

  1. FLAG_ACTIVITY_NEW_TASK 作用是為Activity指定 “SingleTask”啟動模式。跟在AndroidMainfest.xml指定效果同樣。

注意: FLAG_ACTIVITY_NEW_TASK與FLAG_ACTIVITY_CLEAN_TOP才具備AndroidMainfest.xml指定SingleTask同樣效果

  1. FLAG_ACTIVITY_SINGLE_TOP 作用是為Activity指定 “SingleTop”啟動模式,跟在AndroidMainfest.xml指定效果同樣。

  2. FLAG_ACTIVITY_CLEAN_TOP 具有此標記位的Activity,啟動時會將與該Activity在同一任務棧的其他Activity出棧。一般與SingleTask啟動模式一起出現。它會完畢SingleTask的作用。但事實上SingleTask啟動模式預設具有此標記位的作用

  3. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有此標記位的Activity不會出如今歷史Activity的列表中,使用場景:當某些情況下我們不希望使用者通過歷史列表回到Activity時,此標記位便體現了它的效果。它等同於在xml中指定Activity的屬性:android:excludeFromRecents="trure"

相關

  1. Activity因系統記憶體不足被Kill或是因為Crash閃退的異常退出怎麼儲存資料? Activity被銷燬了以後呼叫了onSaveInstanceState來儲存資料,這個只會在Activity即將銷燬並且有機會重新顯示的情況下才會去呼叫onSaveInstanceState,與之配對的是在onRestoreInstanceState裡恢復資料。

  2. 資源不足時Activity的殺死順序

    • 前臺Activity,正在和使用者互動的Activity,優先順序最高
    • 可見但非前臺Activity,比如Activity中彈出了一個對話方塊,導致Activity可見但是位於後臺無法和使用者直接互動。
    • 後臺Activity,已經被暫停的Activity,比如執行了onStop,優先順序最低
  3. 橫豎屏切換時候Activity的生命週期 跟Manifest配置有關

    • 不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期預設首先銷燬當前activity,然後重新建立
    • 設定Activity的android:configChanges="orientation|screenSize"時,切屏不會重新呼叫各個生命週期,只會執onConfigurationChanged方法
  4. 啟動其他應用Activity時,報java.lang.SecurityException: Permission Denial,因為在其他應用StartActivity時,有去檢驗permission。

滿足以下條件,可以成功startActivity,不會出現permission denial:

  • 同一個application下
  • Uid相同
  • permission匹配
  • 目標Activity的屬性Android:exported=”true”
  • 目標Activity具有相應的IntentFilter,存在Action動作或其他過濾器並且沒有設定exported=false
  • 啟動者的Pid是一個System Server的Pid
  • 啟動者的Uid是一個System Uid(android.system.uid=1000)

相關文章