十八、处理活动生命周期事件

众所周知,Android 设备基本上都是手机。因此,有些活动比其他活动更重要——对用户来说,接电话可能比玩数独更重要。而且,因为它是一部手机,它的内存可能比你现在的台式机或笔记本要少。

由于手机内存有限,你的活动可能会被终止,因为其他活动正在进行,系统需要你的活动的内存。可以把它想象成生命循环的机器人版——你的活动结束了,其他人可能会活下来,以此类推。在您认为活动已经完成之前,甚至在用户认为活动已经完成之前,您都不能假设活动将会运行。这是一个例子,也许是最重要的例子,说明了活动的生命周期将如何影响您自己的应用逻辑。

本章涵盖了构成活动生命周期的各种状态和回调,以及如何恰当地挂钩它们。

薛定谔的活动

一般来说,活动在任何时间点都处于四种状态之一:

  • 活动:活动由用户启动,正在运行,在前台。这是你习惯于考虑的活动运作方式。
  • 暂停:活动由用户启动,正在运行,并且是可见的,但是通知或其他东西覆盖了屏幕的一部分。在此期间,用户可以看到您的活动,但可能无法与之互动。示例包括提示用户接受来电,或者警告用户电池电量低或电量极低。
  • 停止:该活动由用户启动,正在运行,但被其他已经启动或切换到的活动隐藏。您的应用不能直接向用户呈现任何有意义的内容,但是可以通过通知的方式进行通信。
  • Dead :要么该活动从未开始(例如,在电话复位之后),要么该活动被终止,可能是由于缺少可用存储器。

生命、死亡和你的活动

Android 使用本节描述的方法调用您的活动,因为活动在上一节列出的四种状态之间转换。一些转换可能会导致多次调用您的活动,有时 Android 会在不调用应用的情况下将其杀死。这整个领域是相当模糊的,可能会发生变化,所以在决定哪些事件值得关注,哪些可以安全忽略时,请密切关注 Android 官方文档以及本节。

请注意,对于所有这些方法,您应该向上链接并调用该方法的超类版本,否则 Android 可能会引发异常。

onCreate()和 onDestroy()

在所有的例子中,我们已经在所有的Activity子类中实现了onCreate()。在三种情况下会调用此方法:

  • 当活动第一次启动时(例如,从系统重启开始),将使用null参数调用onCreate()
  • 如果活动一直在运行,然后过一段时间被终止,onCreate()将被调用,来自onSaveInstanceState()Bundle作为参数(如下一节所述)。
  • 如果 activity 一直在运行,并且您已经将 activity 设置为基于不同的设备状态(例如,横向与纵向)拥有不同的资源,那么您的 activity 将被重新创建,并调用onCreate()。第 23 章中介绍了如何使用资源。

这里是您初始化 UI 和设置任何需要一次性完成的事情的地方,不管活动是如何使用的。

在生命周期的另一端,当活动关闭时,可能会调用onDestroy(),这可能是因为名为finish()的活动(它“结束”了该活动),也可能是因为 Android 需要 RAM 并且过早地关闭了该活动。请注意,如果对 RAM 的需求很紧急(例如,一个来电),可能不会调用onDestroy(),但是活动仍然会被关闭。因此,onDestroy()主要是为了干净地释放你在onCreate()获得的资源(如果有的话)。

在处理包含视图的活动时要小心,该视图由来自数据库(如 SQLite)的适配器填充。谨慎的做法是在数据库和/或适配器对象上调用close(),但是也要记住,如果您的关闭是突然的,那么您不能依赖在onDestroy()中调用这些对象。我们将在第 32 章中进一步讨论这个问题。

onStart()、onRestart()和 onStop()

一个活动可以出现在前台,因为它是第一次被启动,或者因为它在被隐藏(例如,被另一个活动或被一个呼入电话)之后被带回到前台。在这两种情况下都会调用onStart()方法。

在活动已经停止并且现在重新开始的情况下,调用onRestart()方法。

相反,当活动将要停止时,调用onStop()

onPause()和 onResume()

在您的活动进入前台之前调用onResume()方法,无论是在最初启动之后、从停止状态重新启动之后,还是在弹出对话框(例如,来电)被清除之后。这是一个很好的地方,可以根据用户上次查看您的活动后发生的事情来刷新 UI。例如,如果您正在轮询某个服务的某些信息的更改(例如,提要的新条目),onResume()是刷新当前视图以及(如果适用)启动后台线程来更新视图(例如,通过Handler)的好时机。

相反,任何从您的活动中窃取用户的事情——通常是激活另一个活动——都会导致您的onPause()方法被调用。在这里,您应该撤销您在onResume()中所做的任何事情,比如停止后台线程、释放您可能已经获得的任何独占访问资源(例如,相机)等等。

一旦onPause()被调用,Android 保留在任何时候终止你的活动进程的权利。因此,您不应该依赖于接收任何进一步的事件。

国家的恩惠

大多数情况下,前面提到的方法是用于处理应用级的事情(例如,在onCreate()中把你的 UI 的最后部分连接在一起,或者在onPause()中关闭后台线程)。

然而,Android 的很大一部分目标是拥有无缝的铜绿。活动可能根据内存需求来来去去,但理想情况下,用户不会意识到这种情况正在发生。例如,如果一个用户正在使用一个计算器,午休后又回到那个计算器,他应该会看到他在午休前正在处理的数字,除非他采取了一些措施关闭计算器(例如,按下返回按钮退出)。

为了完成所有这些工作,活动需要能够保存它们的应用实例状态,并且要快速、廉价地保存。由于活动可能在任何时候被终止,活动可能需要比您预期的更频繁地保存它们的状态。然后,当活动重新启动时,活动应该恢复到以前的状态,这样它就可以将活动恢复到以前的状态。可以把它想象成建立一个书签,这样当用户返回到该书签时,您可以将应用恢复到用户离开时的状态。

保存实例状态由onSaveInstanceState()处理。这提供了一个Bundle,活动可以将它们需要的任何数据(例如,计算器显示屏上显示的数字)注入其中。这个方法的实现需要很快,所以不要太花哨——只需将数据放入Bundle中,然后退出该方法。

该实例状态在两个地方再次提供给您:在onCreate()onRestoreInstanceState()中。当您希望将状态数据重新应用到活动时,这是您的选择——任一回调都是合理的选择。

onSaveInstanceState()的内置实现将保存部件子集的可能可变状态。例如,它会将文本保存在EditText中,但不会保存Button是启用还是禁用的状态。只要小部件通过它们的android:id属性被唯一识别,这就可以工作。

因此,如果您实现了onSaveInstanceState(),您可以向上链接并利用继承的实现,或者不向上链接并覆盖继承的实现。类似地,有些活动可能根本不需要onSaveInstanceState()来实现,因为内置的活动会处理所有需要的事情。