首页 > 其他分享 >Android应用开发者,你们真的了解Activity的生命周期吗?

Android应用开发者,你们真的了解Activity的生命周期吗?

时间:2023-06-19 12:37:37浏览次数:49  
标签:生命周期 Bundle 开发者 Activity Android 方法 onStop


一开始,我觉得Activity的生命周期虽然过于复杂,但它不应该是一个难题。我的意思是:对于Android开发新手来说,如何正确地处理Activity生命周期可能有点困难,但是我无法想象对于那些富有经验的android开发者来说,这依然是一个棘手的问题。

我还是想的太简单了。

一会儿我会告诉你整个故事,但是先让我简述下我写这篇文章的目的。

我想要与你们分享我是如何处理Activity的生命周期的,它比官方文档里描述的更简单,而且还涵盖绝大多数棘手的极端情形。

Activity的生命周期仍然是一个难题:

一个星期内发生的两件事情让我意识到:即使在今天,Activity的生命周期仍然是Android开发人员面临的一个难题。

几周前一位Redditor在“androiddev”里分享了一篇关于Activity的生命周期的文章。这篇文章的写作基础来源于我在StackOverflow社区里分享的一个答案。当时提问者推荐了一种处理Activity的生命周期的方法,我觉得并不准确,于是我立即提交了一条评论,希望其他读者能意识到这一点。

然后陆续有几位redditor回答并质疑了我的观点,进而演变成了一场漫长而非常有见地的讨论。在讨论过程中,我观察到了一些有趣的关于Activity的生命周期的现象:

  1. Activity的生命周期让许多有经验的android开发人员感到困惑。
  2. 官方文档里仍然存在着一些关于Activity的生命周期的过时信息和矛盾信息。
  3. 即使是编写Google官方教程的开发人员们也没有真正地理解Activity的生命周期以及它的影响。

几天之后,我正在访问一个潜在客户。该公司雇佣了一批富有经验的Android开发人员,他们尝试在老的代码库里添加新功能,这不是一件容易的事。

在快速代码审查期间,我注意到其中一位维护人员决定在Activity的onCreate(Bundle)中订阅EventBus,并在onDestroy()中取消订阅。这样做会带来很大的麻烦,所以我建议他们重构这部分代码。

My take on Activity life-cycle::

上述两件事让我意识到还是有许多android开发人员对Activity的生命周期充满困惑。现在,我想尝试通过分享我的经验来给其他开发人员提供便利。

我不打算在这里为Activity的生命周期重新编写一份文档,这代价太大了。我会告诉你如何在Activity生命周期的各个方法之间划分逻辑以实现最简单的设计并避免最常见的陷阱。

onCreate(Bundle):

Android framework并没有提供Activity的构造函数,它会自动创建Activity类的实例。那么我们应该在哪里初始化Activity呢?

你可以把所有应该在Activity的构造函数里处理的逻辑放在onCreate(Bundle)这个方法里。

理想情况下,构造函数只会初始化对象的成员变量:


public ObjectWithIdealConstructor(FirstDependency firstDependency, 
 SecondDependency secondDependency) {
 mFirstDependency = firstDependency;
 mSecondDependency = secondDependency;
}


除了初始化成员变量外, onCreate(Bundle)方法还承担着另外两个职责:恢复之前保存的状态并调用setContentView()方法。

所以,onCreate(Bundle)方法中的基本功能结构应该是:


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);

 getInjector().inject(this); // inject dependencies

 setContentView(R.layout.some_layout);

 mSomeView = findViewById(R.id.some_view);

 if (savedInstanceState != null) {
 mWelcomeDialogHasAlreadyBeenShown = savedInstanceState.getBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN);
 }

}


显然,这并不适用于所有人,你可能会有一些不同的结构。不过没关系,只要你在onCreate(Bundle)中没有以下内容:

  • Subscription to observables
  • Functional flows
  • Initialization of asynchronous functional flows
  • Resources allocations

每当我需要决定某一段逻辑是否应该放在onCreate(Bundle)方法里时,我就会问自己这个问题:这段逻辑与Activity的初始化有关吗?如果答案是否定的,我就会另寻别处。

当Android framework创建了一个Activity实例,而且它的onCreate(Bundle) 方法执行完毕之后,这个Activity就会处于已创建状态。

处于已创建状态的Activity 并不会触发资源分配,也不会接收到系统里其他对象发出的事件。从这种意义上来讲,“created”状态是一种已经准备好了,但是不活跃和隔离的状态。

onStart():

为了使Activity能够与用户交互,Android framework接着会调用Activity的onStart()方法使其处于活跃状态。onStart() 方法中的一些基本的逻辑处理包括:

  1. Registration of View click listeners
  2. Subscription to observables (general observables, not necessarily Rx)
  3. Reflect the current state into UI (UI update)
  4. Functional flows
  5. Initialization of asynchronous functional flows
  6. Resources allocations

对于第1点和第2点,你可能会感到很惊讶。因为在大多数官方文档和教程里,它们都会被放在onCreate(Bundle)里。我认为这是对复杂的Activity的生命周期的一种误解,我会在接下来的onStop()方法里讲到这一点。

所以,onStart()方法中的基本功能结构应该是:

@Override
public void onStart() {
 super.onStart();

 mSomeView.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 handleOnSomeViewClick();
 }
 });

 mFirstDependency.registerListener(this);

 switch (mSecondDependency.getState()) {
 case SecondDependency.State.STATE_1:
 updateUiAccordingToState1();
 break;
 case SecondDependency.State.STATE_2:
 updateUiAccordingToState2();
 break;
 case SecondDependency.State.STATE_3:
 updateUiAccordingToState3();
 break;
 }

 if (mWelcomeDialogHasAlreadyBeenShown) {
 mFirstDependency.intiateAsyncFunctionalFlowAndNotify();
 } else {
 showWelcomeDialog();
 mWelcomeDialogHasAlreadyBeenShown = true;
 }
}

需要强调的一点是,你实际在onStart()中所执行的操作是由每个特定Activity的详细需求决定的。

在Android framework调用完onStart()方法后,Activity将从已创建状态转换为已启动状态。在此状态下,它可以正常工作,并且可以与系统的其他组件协作。

onResume():

关于onResume()方法的第一条准则是你不需要覆盖onResume()方法。第二条准则也是你不需要覆盖onResume()方法。第三条准则是在某些特殊情况下你才会确实需要覆盖onResume()方法。

我搜索了我的一个项目的代码库,发现有32个onStart()方法被覆盖重写,平均每段代码约5行,一共有大约150多行代码。相反,只有2个onResume()方法被覆盖重写,一共8行代码,这8行代码主要是用于恢复播放动画和视频。

这总结了我对onResume()的看法:它只能用于在屏幕上启动或恢复一些动态的对象。你想在onResume()而不是onStart()中做这件事的原因将在稍后的onPause()部分讨论。

在Android framework调用完onResume()方法后,Activity将从已启动状态转换为"resumed"状态。在此状态下,用户可以与它进行交互。

onPause():

在这个方法里,您应该暂停或停止在onResume()中恢复或启动的动画和视频。就像onResume()一样,你几乎不需要重写onPause()。在onPause()方法而不是onStop()方法中处理动画的原因是因为当Activity被部分隐藏(比如,弹出系统对话框)或者是在多窗口模式下失去焦点的时候,只有onPause()方法会被调用。因此如果你想在 只有用户正在与Activity交互的情况下播放动画,同时避免在多窗口模式下分散用户注意力并延长电池寿命,onPause() 是你唯一可以信赖的选择。 这一结论的前提是你希望你的动画或视频在用户进入多窗口模式时停止播放,如果你希望你的动画或视频在用户进入多窗口模式时继续播放,那么你不应该在onPause()中暂停它。在这种情况下,你需要将onResume()/ onPause()中的逻辑移动到onStart()/ onStop()。

一种特殊的情况是相机的使用,由于相机是所有应用程序共享的单一资源,因此通常您会想要在onPause()方法中释放它。

在Android framework调用完onPause()方法后,Activity将从"resumed"状态转换为已启动状态。

onStop():

在这个方法里,您将注销所有观察者和监听者,并释放onStart()中分配的所有资源。

所以,onStop()方法里的基本功能结构应该是:


@Override
public void onStop() {
 super.onStop();

 mSomeView.setOnClickListener(null);

 mFirstDependency.unregisterListener(this);
}


让我们来讨论一个问题:为什么你需要在这个方法里取消注册?

首先,如果此Activity不被mFirstDependency取消注册的话,您可能会泄漏它。这不是我愿意承担的风险。

所以,问题变成了为什么要在onStop()方法里取消注册,而不是onPause()方法或者是onDestroy()方法?

想要快速清晰地解释这些确实有点棘手。

如果在onPause()方法里调用mFirstDependency.unregisterListener(this),那么Activity将不会收到相关异步流程完成的通知。因此,它不能让用户感知到这一事件,从而完全违背了多窗口模式的设计初衷,这不是一种好的处理方式。

如果在onDestroy()方法里调用mFirstDependency.unregisterListener(this),这同样不是一种好的处理方式。

当应用被用户推到后台(例如,点击“home”按钮)时,Activity的onStop()将被调用,从而使得其返回到“已创建”状态,这个Activity可以在几天甚至几周的时间内保持这个状态。

如果这时候mFirstDependency产生了连续的事件流,那么在这几天甚至几周的时间里,Activity可以都处理这些事件,即使用户在这段时间内从未真正与它交互过。这将是对用户电池寿命的不负责任的浪费,而且在这种情况下,应用消耗的内存会逐渐增多,应用进程被OOM(Out Of Memory)Killer杀死的可能性也会增大。

因此,在onPause()和onDestroy()里调用mFirstDependency.unregisterListener(this)都不是一种好的处理方式,您应该在onStop()中执行此操作。

关于注销View的事件监听器,我想多说几句。

由于Android UI框架的不合理设计,像这个问题中所描述的奇怪场景是可能会发生的。有几种方法可以解决这个问题,但是从onStop()中注销View的事件监听器是最干净的一种。

或者,您可以完全忽略这种罕见的情况。这是大多数Android开发人员所做的,但是,您的用户偶尔会遇到一些奇怪的行为和崩溃。

在Android framework调用完onStop()方法后,Activity将从已启动状态转换为已创建状态。

onDestroy():

永远都不要覆盖重写这个方法。

我搜索了我所有的项目代码库,没有一个地方需要我重写onDestroy()方法。

如果你思考一下我前面对onCreate(Bundle)的职责的描述,你会发现这是完全合理的,它仅仅执行了初始化Activity对象的操作,所以完全没有必要手动完成清理工作。

当我在查看新的Android代码库时,我通常会搜索几个常见的错误模式,这使我能够快速地了解代码的质量。 onDestroy()的覆盖重写是我寻找的第一个错误模式。

onSaveInstanceState(Bundle):

此方法用于保存一些临时的状态,在这个方法里你位移需要做的就是将你想保存的状态存入Bundle数据结构中:


@Override
public void onSaveInstanceState(Bundle outState) {
 super.onSaveInstanceState(outState);
 outState.putBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN, mWelcomeDialogHasAlreadyBeenShown);
}


在这里,我想提一个与Fragment有关的常见陷阱。

如果你在这个方法执行完之后,提交Fragment事务,那么应用程序会抛出IllegalStateException异常而导致崩溃。

你需要知道的一点是onSaveInstanceState(Bundle)将在onStop()之前被调用。

好消息是,经过多年的努力,谷歌终于意识到了这个bug。 在Android P中,onStop()将在onSaveInstanceState(Bundle)之前被调用。

总结:

是时候结束这篇文章了。

虽然本文的内容既不简短也不简单,但我认为它比大多数文档(包括官方教程)更简单,更完整。

我知道一些经验丰富的专业开发人员会质疑我处理Activity生命周期的方式,没关系。事实上,我非常期待您的质疑。

不过,请记住,我已经使用这个方案好几年了。根据我的经验,使用这种方法编写的代码比许多项目中看到的对于Activity生命周期的处理逻辑要清晰简洁的多。

这种方法的最终验证来自Google本身。

从Nougat开始,Android系统支持多屏任务。我在这篇文章中与你分享的方法几乎不需要任何调整。这基本上证实,相对于官方文档,它包含了对Activity生命周期更深入的见解。

另外,Android P中对于Activity的onStop()和onSaveInstanceState(Bundle)方法之间的调用顺序的调整将使得这种方法对于Fragments的使用来说是最安全的。

我不认为这是巧合。

  • 原文地址:Android Activity Life-Cycle for Professional Developers
  • 原文作者:Vasiliy Zukanov

标签:生命周期,Bundle,开发者,Activity,Android,方法,onStop
From: https://blog.51cto.com/u_16163480/6512366

相关文章

  • 【Android面试】2023年Android中高级最全面试题,轻松拿offer
    前言2023年的金三银四来了,不知道大家准备好了没?对于程序员而言,无论是想加快个人技能成长速度,或是想要升职涨薪,都需要不断进阶,跳槽到规模与业务更大、并且有成熟的技术成长体系的公司当中去。而想要跳槽成功,面试可以说是决定性因素。这里给大家整理了一份935页的《2023年Android中高......
  • 使用 koin 作为 Android 注入工具,真香
    koin为Android提供了简单易用的API接口,让你简单轻松地接入koin框架。[koin在Android中的gradle配置]mp.weixin.qq.com/s/bscC7mO4O…1.Application类中startKoin从您的类中,您可以使用该函数并注入Android上下文,如下所示:ApplicationstartKoinandroidContextclass......
  • 布局性能优化:安卓开发者不可错过的性能优化技巧
    今天总结一下布局的性能优化,这是一个系列,上一篇是#内存泄漏大集结:安卓开发者不可错过的性能优化技巧也可以看性能优化专栏里的记录,都是非常好的开发经验。当我们开发Android应用时,布局性能优化是一个必不可少的过程。一个高效的布局能够提高用户体验,使应用更加流畅、响应更加迅速......
  • 收藏向 | 车载Android系统开发学习专题,进军车载必备
    如今,智能手机已无处不在,移动操作系统开始向新的领域扩张,这其中,就有近段时间被苹果CarPlay催热的车载系统。目前全球的汽车数量已经超过10亿辆,但车载系统却仍然有非常大的提升空间。汽车厂商们也早已意识到了车载对于未来汽车市场的重要性,开始布局更加智能的车载系统,让自己的汽车能......
  • 如何有效阅读源码?最新Android开发源码精编解析,优秀程序员必备
    大多数人阅读源码是为了应对面试中可能会提到的相关问题,提高面试的成功率,因此选择源码相关的书籍和视频来看是速成的最好方法。但对于想真正提高编码水平,让自己的事业更上一层楼的开发者而言,只有下功夫、花时间,才能有所突破。不过大家也清楚,阅读源码是比较困难的,尤其是对于项目背景......
  • 监听Activity生命周期方式及案例讲解
    本篇文章主要讲解如何快速实现Activity生命周期监听,以及其在官方lifecycle、第三方库Glide、PermissionX中的应用1.Activity生命周期监听Fragment实现Activity生命周期监听众所周知,Fragment中生命周期分发主要是依赖Activity,所以为了监听Activity的生命周期我们直接添加一个空的Fr......
  • 2022最全面&详细的Android学习指南,零基础快速入门的通道
    随着Android技术栈的不断扩展,不少新的技术如Flutter、RN、小程序等涌入我们的视野,也有越来越多的人投身入Android开发的赛道。无论你是35岁正在遭遇中年职业危机的程序员,还是刚刚毕业的大学生/研究生,想走技术这条路,都需要不断学习,风雨兼程!那么,如何系统的学习Android呢?首先来看看And......
  • Android进阶宝典 -- JetPack Navigation的高级用法(解决路由跳转新建Fragment页面问题)
    相信有相当一部分的伙伴,在项目开发中依然使用Activity作为页面承载体,有10个页面就会有10个Activity,这种方式当然没问题,但是如果涉及到页面间数据共享,那么使用多Activity就不是很方便了,需要Activity传递各种数据,涉及到数据的序列化与反序列化;因此产生了单Activity和多Fragment架构,所......
  • Android开发卷吗?卷;怎么破?从Framework提升自己——字节总监
    前言现在客户端卷的风起云涌,很多安卓开发者都是抱怨连天。内卷之下,相比本来就堪忧的发量,前途未卜的迷茫带来的精神折磨更是雪上加霜。其实平心而论,很多开发者对现有知识都掌握的不够扎实,例如Framework。对于大部分的开发者来说,AndroidFrameWork都无法做到精通,更别提触类旁通了。想......
  • 2022最新 Android 中高级面试题汇总(含答案解析)
    准备面试少不了Android面试题复习,面试题也需要有方法,高效的进行复习,所以给大家分享一份《2022最新Android中高级面试题汇总》,基本涵盖了Android中高级工程师面试必备知识点,希望可以帮助一些想要跳槽涨薪的朋友更好、更高效的复习,实现跳槽涨薪。Android中高级面试题主要包含知识......