1. 程式人生 > 實用技巧 >安卓Activity生命週期(未完)

安卓Activity生命週期(未完)

安卓Activity生命週期

安卓官方文件學習梳理
Date:2021.1.11(待更新)
Author:Vembo
本文概述了安卓Activity生命週期的相關概念,同時介紹了生命週期感知型元件lifecycle以及ViewModel。

當用戶瀏覽、退出和返回到應用時,應用中的 Activity 例項會在其生命週期的不同狀態間轉換。Activity 類會提供許多回調,這些回撥會讓 Activity 知曉某個狀態已經更改:系統正在建立、停止或恢復某個 Activity,或者正在銷燬該 Activity 所在的程序。

1 常規的生命週期

1.1 onCreat()

系統首次建立 Activity 時觸發。在 onCreate() 方法中,您需執行基本應用啟動邏輯,該邏輯在 Activity 的整個生命週期中只應發生一次

一般在這個週期中引入xml,繫結資料

如果您有一個生命週期感知型元件與您的 Activity 生命週期相關聯,該元件將收到 ON_CREATE 事件。系統將呼叫帶有 @OnLifecycleEvent 註釋的方法,以使您的生命週期感知型元件可以執行已建立狀態所需的任何設定程式碼。(參見下一節)

在本示例中,系統通過將檔案的資源 ID R.layout.main_activity 傳遞給 setContentView() 來指定 XML 佈局檔案。

lateinit var textView: TextView
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState)

    // recovering the instance state
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity)

    // initialize member TextView so we can manipulate it later
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// invoked when the activity may be temporarily destroyed, save the instance state here
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // call superclass to save any view hierarchy
    super.onSaveInstanceState(outState)
}

//

1.2 onStart()

當 Activity 進入“已開始”狀態時,系統會呼叫此回撥。

當 Activity 進入已開始狀態時,與 Activity 生命週期相關聯的所有生命週期感知型元件都將收到 ON_START 事件。

一旦此回撥結束,Activity 便會進入“已恢復”狀態,系統將呼叫 onResume() 方法。

1.3 onResume()

應用與使用者互動時,會一直保持這樣的狀態,直到焦點遠離應用,這個時候系統呼叫 onPause() 回撥。

當 Activity 進入已恢復狀態時,與 Activity 生命週期相關聯的所有生命週期感知型元件都將收到 ON_RESUME 事件。這時,生命週期元件可以啟用在元件可見且位於前臺時需要執行的任何功能,例如啟動相機預覽。

class CameraComponent : LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }

    ...
}

LifecycleObserver 收到 ON_RESUME 事件後,上述程式碼便會初始化相機。然而,在多視窗模式下,即使處於“已暫停”狀態,您的 Activity 也可能完全可見。例如,當用戶處於多視窗模式,並點按另一個不包含 Activity 的視窗時,您的 Activity 將進入“已暫停”狀態。如果您希望相機僅在應用處於“已恢復”(可見且在前臺執行)狀態時可用,請在收到上述 ON_RESUME 事件後初始化相機。如果您希望在 Activity 處於“已暫停”狀態但可見時(例如在多視窗模式下)保持相機可用,應在收到 ON_START 事件後初始化相機

1.4 onPause()

系統將此方法視為使用者將要離開 Activity的第一個標誌(儘管這並不總是意味著 Activity 會被銷燬);此方法表示 Activity 不再位於前臺(儘管在使用者處於多視窗模式時 Activity 仍然可見)。

當 Activity 進入已暫停狀態時,與 Activity 生命週期相關聯的所有生命週期感知型元件都將收到 ON_PAUSE 事件。這時,生命週期元件可以停止在元件未位於前臺時無需執行的任何功能,例如停止相機預覽。

您還可以使用 onPause() 方法釋放系統資源、感測器(例如 GPS)手柄,或當您的 Activity 暫停且使用者不需要它們時仍然可能影響電池續航時間的任何資源。

class CameraComponent : LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }

    ...
}
  • Tip: onPause() 方法不一定要有足夠的時間來執行儲存操作

1.5 onStop()

如果 Activity 不再對使用者可見,說明其已進入“已停止”狀態,因此係統將呼叫 onStop() 回撥。例如,當新啟動的 Activity 覆蓋整個螢幕時,可能會發生這種情況。

當 Activity 進入已停止狀態時,與 Activity 生命週期相關聯的所有生命週期感知型元件都將收到 ON_STOP 事件。這時,生命週期元件可以停止在元件未顯示在螢幕上時無需執行的任何功能。

如果您無法找到更合適的時機來將資訊儲存到資料庫,可以在 onStop() 期間執行此操作。以下示例展示了 onStop() 的實現,它將草稿筆記內容儲存到永續性儲存空間中:

override fun onStop() {
    // call the superclass method first
    super.onStop()

    // save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // do this update in background on an AsyncQueryHandler or equivalent
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}
  • 應該改用 Room,這是一個通過 SQLite 提供抽象層的永續性庫。

  • 當您的 Activity 進入“已停止”狀態時,Activity 物件會繼續駐留在記憶體中:該物件將維護所有狀態和成員資訊,但不會附加到視窗管理器。Activity 恢復後,Activity 會重新呼叫這些資訊。您無需重新初始化在任何回撥方法導致 Activity 進入“已恢復”狀態期間建立的元件。系統還會追蹤佈局中每個 View 物件的當前狀態,如果使用者在 EditText 微件中輸入文字,系統將保留文字內容,因此您無需儲存和恢復文字。

1.6 onDestroy()

銷燬 Ativity 之前,系統會先呼叫 onDestroy()。系統呼叫此回撥的原因如下:

  1. Activity 即將結束(由於使用者徹底關閉 Activity 或由於系統為 Activity 呼叫 finish()),或者
  2. 由於配置變更(例如裝置旋轉或多視窗模式),系統暫時銷燬 Activity
  • Tip:裝置旋轉onDestroy()

當 Activity 進入已銷燬狀態時,與 Activity 生命週期相關聯的所有生命週期感知型元件都將收到 ON_DESTROY 事件。這時,生命週期元件可以在 Activity 被銷燬之前清理所需的任何資料。

您應使用 ViewModel 物件來包含 Activity 的相關檢視資料,而不是在您的 Activity 中加入邏輯來確定 Activity 被銷燬的原因。如果因配置變更而重新建立 Activity,ViewModel 不必執行任何操作,因為系統將保留 ViewModel 並將其提供給下一個 Activity 例項。如果不重新建立 Activity,ViewModel 將呼叫 onCleared() 方法,以便在 Activity 被銷燬前清除所需的任何資料。可以使用 isFinishing() 方法區分這兩種情況。

  • isFinishing()檢查此活動是否正在完成過程中,或者因為您呼叫它或其他人已要求它完成。這通常用於確定活動是簡單地暫停還是完全完成。‎finish()onPause()

2 LifeCycle

生命週期感知型元件可執行操作來響應另一個元件(如 Activity 和 Fragment)的生命週期狀態的變化。這些元件有助於您寫出更有條理且往往更精簡的程式碼,這樣的程式碼更易於維護。
在使用生命週期感知型元件前需要新增依賴如下:

\\需要新增的依賴(2021.1適用)
\\https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies

dependencies {
    def lifecycle_version = "2.2.0"
    def arch_version = "2.1.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

    // Saved state module for ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

    // Annotation processor
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
    // alternately - if using Java8, use the following instead of lifecycle-compiler
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    // optional - helpers for implementing LifecycleOwner in a Service
    implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "androidx.arch.core:core-testing:$arch_version"
}

2.1 儲存介面狀態--ViewModel 概覽

Activity 的介面狀態在整個配置變更(例如旋轉或切換到多視窗模式)期間,系統會在發生此類配置更改時銷燬 Activity,從而清除儲存在 Activity 例項中的任何介面狀態,使用者暫時從您的應用切換到其他應用,並在稍後返回您的應用,他們也希望介面狀態保持不變。在大多數情況下,您應同時使用 ViewModel 和 onSaveInstanceState()。

ViewModel 已儲存例項狀態 永續性儲存空間
儲存位置 在記憶體中 已序列化到磁碟 在磁碟或網路上
在配置更改後繼續存在
在系統發起的程序終止後繼續存在
在使用者完成 Activity 關閉/onFinish() 後繼續存在
資料限制 支援複雜物件,但是空間受可用記憶體的限制 僅適用於基元型別和簡單的小物件,例如字串 僅受限於磁碟空間或從網路資源檢索的成本/時間
讀取/寫入時間 快(僅限記憶體訪問) 慢(需要序列化/反序列化和磁碟訪問) 慢(需要磁碟訪問或網路事務)

ViewModel指南
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

ViewModel 類旨在以注重生命週期的方式儲存和管理介面相關的資料。ViewModel 類讓資料可在發生螢幕旋轉等配置更改後繼續留存。
當用戶退出您的 Activity 或 Fragment 時,或者在您呼叫 finish() 的情況下,系統會自動銷燬 ViewModel,這意味著狀態會被清除,正如使用者在這些場景中所預期的一樣。ViewModel 將一直留在記憶體中,直到限定其存在時間範圍的 Lifecycle 永久消失:對於 Activity,是在 Activity 完成時;而對於 Fragment,是在 Fragment 分離時。ViewModel 存在的時間範圍是從您首次請求 ViewModel 直到 Activity 完成並銷燬。

2.1.1 實現 ViewModel

架構元件為介面控制器提供了 ViewModel 輔助程式類,該類負責為介面準備資料。在配置更改期間會自動保留 ViewModel 物件,以便它們儲存的資料立即可供下一個 Activity 或 Fragment 例項使用。例如,如果您需要在應用中顯示使用者列表,請確保將獲取和保留該使用者列表的責任分配給 ViewModel,而不是 Activity 或 Fragment,如以下示例程式碼所示:

    class MyViewModel : ViewModel() {
        private val users: MutableLiveData<List<User>> by lazy {
            MutableLiveData().also {
                loadUsers()
            }
        }

        fun getUsers(): LiveData<List<User>> {
            return users
        }

        private fun loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }

然後,您可以從 Activity 訪問該列表,如下所示:

    class MyActivity : AppCompatActivity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            // Create a ViewModel the first time the system calls an activity's onCreate() method.
            // Re-created activities receive the same MyViewModel instance created by the first activity.

            // Use the 'by viewModels()' Kotlin property delegate
            // from the activity-ktx artifact
            val model: MyViewModel by viewModels()
            model.getUsers().observe(this, Observer<List<User>>{ users ->
                // update UI
            })
        }
    }

2.1.2 在 Fragment 之間共享資料

兩個 Fragment 可以使用其 Activity 範圍共享 ViewModel 來處理通訊,如以下示例程式碼所示:

    class SharedViewModel : ViewModel() {
        val selected = MutableLiveData<Item>()

        fun select(item: Item) {
            selected.value = item
        }
    }

    class MasterFragment : Fragment() {

        private lateinit var itemSelector: Selector

        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            itemSelector.setOnClickListener { item ->
                // Update the UI
            }
        }
    }

    class DetailFragment : Fragment() {

        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
                // Update the UI
            })
        }
    }

2.2 待續