安卓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()。系统调用此回调的原因如下:
- Activity 即将结束(由于用户彻底关闭 Activity 或由于系统为 Activity 调用 finish()),或者
- 由于配置变更(例如设备旋转或多窗口模式),系统暂时销毁 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
})
}
}