使用调试日志在 Activity 和 View Model 中演示 Android 生命周期的简单应用

本文详细探讨了Android活动中DefaultLifecycleObserver的实现,展示了如何通过日志打印演示活动生命周期。此外,解释了透明活动如何影响活动的暂停状态。还讨论了ViewModel的生命周期,指出ViewModel的创建和销毁只发生在构造函数和onCleared()方法中,并通过示例展示了在不同屏幕间导航时ViewModel的创建和销毁情况。最后,强调ViewModel的生命周期与Compose导航和ViewModelStoreOwner的关系。

该应用程序有 2 个屏幕(第一个屏幕和第二个屏幕)使用简单的撰写导航实现。

在屏幕之间导航对活动生命周期没有影响。但是,它可能会影响 View Model 生命周期,具体取决于您在何处实例化ViewModel.

我们先来看看Activity的生命周期。

实现 DefaultLifecycleObserver

这是官方的活动生命周期图,表示这些生命周期事件回调的调用时间。例如,在活动进入创建状态之前,onCreate()会调用事件回调。这适用于其余的生命周期状态。

请注意,onCreate()其余的是生命周期事件,而不是生命周期状态。

为了演示活动生命周期,您需要实现DefaultLifecycleObserver接口。然后,您覆盖所有函数并打印出不同的生命周期状态。

class MyLifeCycleObserver(private val name: String) : DefaultLifecycleObserver {

    private val tag = "LifeCycleDebug"

    override fun onCreate(owner: LifecycleOwner) {
        Log.d(tag, "$name: onCreate()")
    }

    override fun onStart(owner: LifecycleOwner) {
        Log.d(tag, "$name: onStart()")
    }

    override fun onResume(owner: LifecycleOwner) {
        Log.d(tag, "$name: onResume()")
    }

    override fun onPause(owner: LifecycleOwner) {
        Log.d(tag, "$name: onPause()")
    }

    override fun onStop(owner: LifecycleOwner) {
        Log.d(tag, "$name: onStop()")
    }

    override fun onDestroy(owner: LifecycleOwner) {
        Log.d(tag, "$name: onDestroy()")
    }
}

onCreate()在活动中,您在函数中注册此生命周期观察者。

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        val observer = MyLifeCycleObserver(MainActivity::class.simpleName!!)
        lifecycle.addObserver(observer)

        /*...*/
        }
    }
}

由于某些原因,onRestart()回调在DefaultLifecycleObserver. 所以你还需要覆盖onRestart()你的活动。

class MainActivity : ComponentActivity() {
    /*...*/

    override fun onRestart() {
        super.onRestart()

        Log.d(
            "LifeCycleDebug",
            "${MainActivity::class.simpleName!!}: onRestart()")
    }
}

模拟活动失去焦点

为了演示当前活动生命周期已暂停(失去焦点),当前活动需要进入后台,但仍然可见。为此,您需要启动第二个具有透明背景的活动。

首先,您创建这个Loses Focus按钮来启动第二个活动。

val context = LocalContext.current
/*...*/
DefaultButton(
    text = "Loses Focus",
    onClick = {
        context.startActivity(Intent(context, SecondActivity::class.java))
    }
)

在AndroidManifest.xml中,添加第二个活动android:theme=“@android:style/Theme.Translucent”:

<activity
    android:name="com.example.understandlifecyclesdemo.ui.SecondActivity"
    android:exported="true"
    android:theme="@android:style/Theme.Translucent">
</activity>

单击按钮时,当前活动失去焦点。因此,它进入暂停状态。

活动生命周期摘要

尝试使用不同的场景并研究 Logcat 的输出。

这是所有不同场景的摘要。

设想活动生命周期事件回调
启动onCreate() → onStart() → onResume()
导航到不同的屏幕无过渡
开始第二个透明活动暂停()
按后退按钮(来自透明活动) onResume()
旋转屏幕onPause() → onStop() → onDestroy() → onCreate() → onStart() → onResume()
按主页按钮onPause() → onStop()
按方形按钮并选择应用程序onRestart() → onStart() → onResume()
关机(按返回键)onPause() → onStop() → onDestroy()
模拟进程死亡(按home键,手动杀死进程)onPause() → onStop()

请注意,在进程死亡时,不会触发 onDestroy() 事件回调。

要模拟进程死亡,您首先需要按主页按钮将活动移动到后台。之后,您手动终止该进程。终止进程的最简单方法是使用 logcat 中的停止按钮。

下面对每个生命周期状态进行更详细的描述:

活动生命周期状态描述
已创建活动已创建但不可见
开始活动在后台可见
已恢复活动在前台可见
毁坏活动被用户退出/关闭

请注意,没有Paused,Stopped和Restarted生命周期状态,我觉得这有点令人困惑。见下图。

ON_PAUSE 事件将生命周期状态发送到 STARTED。ON_STOP 事件将生命周期状态发送到 CREATED。

生命周期事件生命周期状态描述
ON_PAUSE开始活动已暂停且可见,仍处于前台
ON_STOP已创建活动不可见,从前台移动到后台
不适用重新启动Stopped 和 Started 阶段之间的中间状态

现在,让我们看看 View Model 生命周期。

ViewModel 何时创建和销毁?

View Model 中只有 2 个生命周期阶段:

  • ViewModel被建造。那是调用ViewModel 构造函数的时候。
  • ViewModel被摧毁。那是ViewModel.onCleared()被调用的时候。
class MainViewModel(private val name: String) : ViewModel() {  

    private val tag = "LifeCycleDebug"  

    init {  
        Log.d(tag, "${name}ViewModel: onCreated()")  
    }  

    override fun onCleared() {  
        super.onCleared()  

        Log.d(tag, "${name}ViewModel: onCleared()")  
    }  
}

由于MainViewModel接受构造函数参数(以区分ViewModel实例),因此您需要创建MainViewModelFactory

class MainViewModelFactory(private val name: String)  
    : ViewModelProvider.NewInstanceFactory() {  

    override fun <T : ViewModel> create(modelClass: Class<T>): T {  

        if (modelClass.isAssignableFrom(MainViewModel::class.java))  
            return MainViewModel(name) as T  

    throw IllegalArgumentException("Unknown ViewModel class")  
    }  
}

为了演示此视图模型生命周期,您ViewModel在 3 个不同的地方创建:

在 MainScreen() 中创建 ViewModel

@Composable  
private fun MainScreen() {  
    //view model store owner belongs to the activity
    val viewModel: MainViewModel = viewModel(
        factory = MainViewModelFactory("MainScreen"))  
    /*...*/
}

在 FirstScreen() 中创建 ViewModel

@Composable  
fun FirstScreen(  
    navigateToSecondScreen: () -> Unit  
) {  
    //compose navigation creates a new view model store owner for each destination  
  val viewModel: MainViewModel = viewModel(  
        factory = MainViewModelFactory("FirstScreen"))
  /*...*/
}

在 SecondScreen() 中创建 ViewModel

@Composable  
fun SecondScreen(  
    popBackStack: () -> Unit,  
) {  
    //compose navigation creates a new view model store owner for each destination  
  val viewModel: MainViewModel = viewModel(  
        factory = MainViewModelFactory("SecondScreen"))
}

查看模型生命周期摘要

让我们调查一下 Logcat,看看会发生什么。

设想查看模型生命周期状态
启动活动恢复后,创建主屏幕视图模型和第一个屏幕视图模型
导航到第二个屏幕创建第二个屏幕视图模型
弹回第一个屏幕第二个屏幕视图模型被破坏
旋转屏幕对查看模型生命周期没有影响
按返回并退出应用程序Activity 被销毁后,主屏幕视图模型和第一个屏幕视图模型被销毁

ViewModelStoreOwner确定视图模型生命周期。它设置为LocalViewModelStoreOwner.current取决于您调用viewModel()可组合函数的位置。

在MainScreen()中,在构建导航图之前,ViewModelStoreOwner属于活动。在FirstScreen()和SecondScreen()中,组合导航会ViewModelStoreOwner为每个屏幕目的地创建一个新的。然而,由于 First Screen 是根/起始目的地,它的生命周期与主屏幕视图模型非常相似,主屏幕视图模型与活动生命周期相关联。

因此,在活动恢复后,主屏幕视图模型和第一屏幕视图模型都被创建。第二个屏幕视图模型在被推送到堆栈时创建。当它从堆栈中弹出时,它会被销毁。

当应用程序关闭时,活动被破坏。之后,Main 和 First Screen 视图模型被销毁。下图总结了发生的情况。

结论

活动生命周期很常见并且有据可查。但是,视图模型的生命周期并不完全清楚,尤其是当它被销毁时。

在我对这个简单的应用程序进行测试之前,我的印象是,当可组合屏幕离开屏幕时,它的视图模型就会被破坏。我认为视图模型生命周期与可组合屏幕相关联。

好吧,事实并非如此。它基于导航的返回堆栈。当屏幕被添加到后台堆栈时,它的视图模型被创建。当它从后台堆栈中移除时,它的视图模型被破坏。

如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。

小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,需要一份小编整理出来的学习资料的关注我主页或者点击文末卡片免费领取~

这里是关于我自己的Android 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值