简介
在此 Codelab 中,您将了解 Android 的基本组成部分:activity 和 fragment 生命周期。activity 生命周期是 activity 在其整个存在期间可以处于的一组状态。生命周期涵盖从 activity 最初创建一直到它被销毁以及系统收回相应 activity 资源的过程。当用户在应用中的 activity 之间导航(以及进入或退出应用)时,这些 activity 都会在 activity 生命周期的不同状态之间转换。
fragment 生命周期与 activity 生命周期非常相似。此 Codelab 主要介绍 activity,在最后会简要介绍 fragment。
作为 Android 开发者,您需要了解 activity 生命周期。如果您的 activity 无法正确响应生命周期状态变化,您的应用可能会生成奇怪的错误、出现让您的用户感到困惑的行为,或使用过多的 Android 系统资源。了解 Android 生命周期并正确响应生命周期状态变化,对于成为 Android 优秀公民至关重要。
您应当已掌握的内容
- 什么是 activity,以及如何在应用中创建 activity。
- activity 的
onCreate()
方法的用途,以及在该方法中执行的那类操作。 - 如何为 activity 创建 XML 布局,以及如何在运行时更新布局。
学习内容
- 如何将日志记录信息输出到 Android Studio Logcat 窗口(有时称为 Android 控制台或 Android Monitor)。
Activity
和Fragment
生命周期的基础知识,以及当 activity 在不同生命周期状态之间切换时调用的回调。- 如何替换生命周期回调方法,以便在 activity 生命周期的不同时间执行操作。
- 如何在应用中使用
Timber
库进行日志记录。
实践内容
- 修改名为 DessertClicker 的入门应用,以添加在 Logcat 中显示的日志记录信息。
- 替换生命周期回调方法并记录对 activity 状态的更改。
- 运行应用,并记下 activity 启动、停止和恢复时显示的日志记录信息。
- 修改应用以使用
Timber
库。 - 向 AndroidTrivia 应用添加日志记录信息,并监控 fragment 状态的变更。
在此 Codelab 中,您将使用一款名为 DessertClicker 的初始应用。在此应用中,每当用户点按屏幕上的甜点时,该应用就会为用户“购买”相应甜点。应用会在布局中更新已购甜点的数量值,以及用户消费的总金额。
此应用存在多个与 Android 生命周期有关的 bug:例如,在某些情况下,此应用会将甜点数量值重置为 0;此外,此应用即使在后台运行时也会继续使用系统资源。了解 Android 生命周期可以帮助您理解这些问题发生的原因以及解决方法。
每个 activity 和每个 fragment 都具有生命周期。这个词源于动物的生命周期,就像这只蝴蝶的生命周期:蝴蝶的不同状态显示了它从出生到完全成年再到死亡的成长过程。
同样,activity 生命周期由 activity 可能会经历的不同状态组成:从 activity 首次初始化,到最终销毁,再由系统收回其内存。当用户启动您的应用、在 activity 之间导航、在应用内部和外部导航时以及离开应用时,activity 会切换状态。下图显示了 activity 生命周期的所有状态。顾名思义,这些状态表示 activity 所处的状态。
在 activity 生命周期状态发生变化时,您通常需要更改某些行为,或者运行一些代码。因此,Activity
类本身以及 Activity
的任何子类(例如 AppCompatActivity
)都会实现一组生命周期回调方法。Android 会在 activity 从一种状态切换为另一种状态时调用这些回调,而您可以在自己的 activity 中替换这些方法,通过执行任务来响应这些生命周期状态变化。下图显示了生命周期状态以及可用的可替换回调。
fragment 也具有生命周期。fragment 的生命周期与 activity 的生命周期类似,因此您学到的很多知识都适用于这两者。在此 Codelab 中,您将集中了解 activity 生命周期,因为它是 Android 的基本组成部分,并且最易于在简单应用中观察。以下是相应的 fragment 生命周期图:
请务必了解要在何时调用这些回调,以及在每个回调方法中应执行的操作。但这两个图都很复杂,可能会令人困惑。在此 Codelab 中,您并不只是要了解每种状态和回调的含义,而是需要进行一些检测工作,逐渐了解发生的情况。
第 1 步:检查 onCreate() 方法并添加日志记录
为了弄清 Android 生命周期的一些情况,了解何时调用各种生命周期方法会很有帮助。这有助于您找出 DessertClicker 中出现问题的地方。
一种简单的方式是使用 Android 日志记录 API。通过日志记录功能,您可以在应用运行期间向控制台写入简短的消息,并利用它们来显示不同回调的触发时间。
- 下载 DessertClicker 起始应用,并在 Android Studio 中打开它。
- 编译并运行应用,然后在甜点图片上点按几次。请注意 Desserts Sold 的值与美元总金额发生的变化。
- 打开
MainActivity.kt
并检查此 activity 的onCreate()
方法
override fun onCreate(savedInstanceState: Bundle?) {
...
}
在 activity 生命周期图中,您可能认出了 onCreate()
方法,因为您之前使用过此回调。这便是每个 activity 都必须实现的方法。您可使用 onCreate()
方法为 activity 执行所有一次性初始化。例如,在 onCreate()
中,您可以膨胀布局、定义点击监听器或设置数据绑定。
系统会在初始化 activity 之后(在内存中创建新的 Activity
对象后)立即调用一次 onCreate()
生命周期方法。执行 onCreate()
后,相应 activity 会被视为已创建。
- 在
onCreate()
方法中,在对super.onCreate()
的调用后,紧接着添加下面一行代码。根据需要导入Log
类。(按Alt+Enter
,或者在 Mac 上按Option+Enter
,然后选择 Import。)
Log.i("MainActivity", "onCreate Called")
Log
类会将消息写入 Logcat。此命令包含三个部分:
- 日志消息的严重程度,即消息的重要性。在本示例中,
Log.i()
方法会写入一条信息性消息。Log
类中的其他方法包括Log.e()
(表示错误)或Log.w()
(表示警告)。 - 日志标签,在本示例中为
"MainActivity"
。该标签是一个字符串,可让您更轻松地在 Logcat 中找到自己的日志消息。该标签通常是类的名称。 - 实际的日志消息,即一个简短的字符串,在本示例中为
"onCreate called"
。
- 编译并运行 DessertClicker 应用。当您点按甜点后,应用行为没有出现任何变化。在 Android Studio 中,点击屏幕底部的 Logcat 标签页。
Logcat 是用于记录消息的控制台。来自 Android 的关于您应用的消息会出现在这里,包括您使用 Log.i()
方法或其他 Log
类方法显式发送到日志的消息。6. 在 Logcat 窗格的搜索字段中输入 I/MainActivity
。
Logcat 可能包含很多消息,大多数消息对您而言都无用。您可以通过多种方式过滤 Logcat 条目,但搜索是最简单的方式。由于您在代码中使用了 MainActivity
作为日志标签,因此您可以使用该标签来过滤日志。在开头添加 I/
表示这是一条信息性消息,由 Log.i()
创建。
您的日志消息包含日期、时间、软件包名称 (com.example.android.dessertclicker
)、您的日志标签(开头为 I/
)以及实际消息。由于此消息出现在日志中,所以您会知道 onCreate()
已执行。
第 2 步:实现 onStart() 方法
系统会在调用 onCreate()
生命周期方法之后立即调用 onStart()
。onStart()
运行后,您的 activity 会显示在屏幕上。与为初始化 activity 而仅调用一次的 onCreate()
不同,onStart()
可在 activity 的生命周期内多次调用。
请注意,onStart()
需要与相应的 onStop()
生命周期方法配对使用。如果用户启动您的应用后又返回设备的主屏幕,相应 activity 会停止,并且不会再在屏幕上显示。
- 在 Android Studio 中,打开
MainActivity.kt
,依次选择 Code > Override Methods,或按Control+o
。此时,系统会显示一个对话框,其中包含您可以在此类中替换的所有方法的庞大清单。 - 开始输入
onStart
以搜索所需的方法。如需滚动到下一个匹配项,请使用向下箭。从列表中选择onStart()
,然后点击 OK 插入样板替换代码。代码如下所示:
override fun onStart() {
super.onStart()
}
- 在
onStart()
方法中,添加一条日志消息:
override fun onStart() {
super.onStart()
Log.i("MainActivity", "onStart Called")
}
- 编译并运行 DessertClicker 应用,然后打开 Logcat 窗格。在搜索字段中输入
I/MainActivity
,以过滤日志。请注意,onCreate()
和onStart()
方法会依次调用,并且您的 activity 会显示在屏幕上。 - 按设备上的主屏幕按钮,然后使用“最近使用的应用”屏幕返回相应 activity。请注意,activity 会从上次停止的位置恢复,同时使用所有相同的值,并且
onStart()
会再次记录到 Logcat 中。另请注意,系统通常不会再次调用onCreate()
方法。
在此任务中,您将修改应用以使用名为 Timber
的热门日志记录库。与内置的 Android Log
类相比,Timber
具有多项优势。具体而言,Timber
库具有下列优势:
- 可以根据类名称为您生成日志标签。
- 有助于避免在您的 Android 应用的发布版本中显示日志。
- 支持与崩溃报告库集成。
您可以立即见证到第一项优势;其他几项优势会让您在制作和运送更庞大的应用时大获裨益。
第 1 步:将 Timber 添加到 Gradle
- 转到 GitHub 上的 Timber 项目,然后复制 Download 标题下以
implementation
一词打头的那行代码。代码行可能如下所示,但版本号可能不同。
implementation 'com.jakewharton.timber:timber:4.7.1'
- 在 Android Studio 的“Project: Android”视图中,展开 Gradle Scripts,并打开 build.gradle (Module: app) 文件。
- 在依赖项部分中,粘贴您复制的那行代码。
dependencies {
...
implementation 'com.jakewharton.timber:timber:4.7.1'
}
- 点击 Android Studio 右上角的 Sync Now 链接重建 Gradle。构建应该能顺利执行,不会出现任何错误。
第 2 步:创建 Application 类并初始化 Timber
在此步骤中,您将创建一个 Application
类。Application
是一个基类,包含整个应用的全局应用状态。它也是操作系统用于与您的应用交互的主要对象。如果您未指定 Application
类,Android 会使用默认的 Application 类,因此,您的应用始终存在 Application
对象,无需您执行任何特殊操作来创建它。
Timber
使用 Application
类,因为整个应用都将使用此日志记录库,并且此库需要先初始化一次,然后才能完成其他一切设置。在此类情况下,您可以子类化 Application
类,并使用您自己的自定义实现来替换默认实现。
创建 Application
类后,您需要在 Android 清单中指定该类。
- 在
dessertclicker
软件包中,创建一个名为ClickerApplication
的新 Kotlin 类。为此,请展开 app > java,然后右键点击 com.example.android.dessertclicker。依次选择 New > Kotlin File/Class。 - 将该类命名为 ClickerApplication,并将 Kind 设置为 Class。点击 OK。
Android Studio 会创建一个新的 ClickerApplication
类,并在代码编辑器中打开它。代码如下所示:
package com.example.android.dessertclicker
class ClickerApplication {
}
- 将类定义更改为
Application
的子类,并在必要时导入Application
类。
class ClickerApplication : Application() {
- 如需替换
onCreate()
方法,请依次选择 Code > Override Methods,或按Control+o
。
class ClickerApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}
- 在
onCreate()
方法中,初始化Timber
库:
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
}
这行代码会为您的应用初始化 Timber
库,以便您在 activity 中使用该库。
- 打开 AndroidManifest.xml。
- 在
<application>
元素的顶部,为ClickerApplication
类添加一个新属性,以便 Android 知悉要使用您的Application
类,而不是默认 Application 类。
<application
android:name=".ClickerApplication"
...
第 3 步:添加 Timber 日志语句
在此步骤中,您将更改 Log.i()
调用以使用 Timber
,然后为其他所有生命周期方法实现日志记录功能。
- 打开
MainActivity
并滚动到onCreate()
。将Log.i()
替换为Timber.i()
并移除日志标签。
Timber.i("onCreate called")
与 Log
类一样,Timber
也使用 i()
方法处理信息性消息。请注意,如果使用 Timber
,则无需添加日志标签;Timber
会自动将该类的名称用作日志标签。
- 同样,更改
onStart()
中的Log
调用:
override fun onStart() {
super.onStart()
Timber.i("onStart Called")
}
- 编译并运行 DessertClicker 应用,然后打开 Logcat。请注意,对于
onCreate()
和onStart()
,您仍会看到相同的日志消息,只是现在生成这些消息的是Timber
,而不是Log
类。 - 替换
MainActivity
中其余的生命周期方法,然后为每种方法添加Timber
日志语句。代码如下:
override fun onResume() {
super.onResume()
Timber.i("onResume Called")
}
override fun onPause() {
super.onPause()
Timber.i("onPause Called")
}
override fun onStop() {
super.onStop()
Timber.i("onStop Called")
}
override fun onDestroy() {
super.onDestroy()
Timber.i("onDestroy Called")
}
override fun onRestart() {
super.onRestart()
Timber.i("onRestart Called")
}
- 再次编译并运行 DessertClicker,然后检查 Logcat。这一次请注意,除了
onCreate()
和onStart()
之外,还有一条有关onResume()
生命周期回调的日志消息。
当 activity 从头开始启动时,您会看到系统按顺序调用以下三个生命周期回调:
onCreate()
:用于创建应用。onStart()
:用于启动相应 activity,并让其在屏幕上显示。onResume()
:用于使相应 activity 成为焦点,并让用户能够与其互动。
onResume()
方法尽管名称是这样,但会在启动时调用,即使没有要恢复的 activity 也是如此。
现在,DessertClicker 应用已设置好日志记录功能,您可以随时通过各种方式开始使用该应用,并且可以探索如何通过触发生命周期回调来响应这些使用行为。
用例 1:打开和关闭 activity
您可以先从最基本的用例入手,也就是首次启动您的应用,然后完全关闭应用。
- 编译并运行 DessertClicker 应用(如果该应用尚未运行)。正如您所看到的,当 activity 首次启动时,系统会调用
onCreate()
、onStart()
和onResume()
回调。 - 多次点按纸杯蛋糕。
- 点按设备上的返回按钮。请注意,在 Logcat 中,系统将按上述顺序调用
onPause()
、onStop()
和onDestroy()
。
在本示例中,使用返回按钮会导致 activity(和应用)完全关闭。执行 onDestroy()
方法意味着相应 activity 已完全关闭,可以进行垃圾回收。垃圾回收是指自动清理您不再使用的对象。调用 onDestroy()
后,操作系统会知道这些资源是可丢弃的,然后开始清理这部分内存。
如果您的代码手动调用该 activity 的 finish()
方法,或者用户强行退出该应用(例如,用户通过在“最近”屏幕点击窗口一角的 X 强行退出),您的 activity 也可能会完全关闭。如果您的应用长时间没有在屏幕上显示,Android 系统也可能会自行关闭您的 activity。Android 这样做是为了节省电量,同时允许其他应用使用您应用的资源。
- 使用“最近”屏幕返回该应用。Logcat 如下所示:
相应 activity 在上一步中已被销毁,因此当您返回该应用时,Android 会启动一个新的 activity 并调用 onCreate()
、onStart()
和 onResume()
方法。请注意,您在上一个 activity 中订购的甜点的数量和总价尚未保留。它们已重置为零。您可能不希望自己的应用出现此行为。我们会在后续的 Codelab 中解决此问题。
此处的要点是,在单个 activity 实例的生命周期内,onCreate()
和 onDestroy()
仅调用了一次:onCreate()
用于首次初始化应用,onDestroy()
则用于清理您的应用所用的资源。
onCreate()
方法是重要的一步;在此方法中,您会执行所有的首次初始化,通过膨胀首次设置其布局,以及对变量进行初始化。
用例 2:离开和返回 activity
现在,您已经启动了应用并将其完全关闭,在此过程中,您看到了 activity 首次创建时经历的大部分生命周期状态。此外,您还看到了 activity 在完全关闭和销毁过程中经历的所有生命周期状态。但当用户与其 Android 设备交互时,他们会执行各种操作,例如在应用之间切换、返回主屏幕、启动新应用以及处理由通话等其他外部 activity 导致的中断。
每次用户离开您的 activity 时,它都不会关闭:
- 当 activity 不再在屏幕上可见时,就说明该 activity 已置于后台(与之相反的是 activity 位于前台或屏幕上)。
- 当用户返回您的应用时,相应 activity 会重启并再次可见。这部分生命周期称为应用的可见生命周期。
当您的应用位于后台时,为保留系统资源和延长电池续航时间,应用不应活跃运行。您可以使用 Activity
生命周期及其回调来了解应用何时切换到后台,以便您暂停任何正在进行的操作。然后,在您的应用进入前台时重启这些操作。
例如,假设某个应用需要大量使用计算资源。此应用可能会使用设备的 CPU 进行许多计算。移动设备的处理能力和电池续航时间通常受到限制,因此 Android 运行时系统需要均衡资源。由于后台进程可能会降低性能或过早耗尽手机电量,因此 Android 可能会阻止未在前台运行的应用使用资源。
在下一步中,您将查看当应用进入后台以及返回前台时的 activity 生命周期。
- 在 DessertClicker 应用运行时,点击几次纸杯蛋糕。
- 按设备上的主屏幕按钮,然后在 Android Studio 中观察 Logcat。请注意,系统会调用
onPause()
方法和onStop()
方法,但不会调用onDestroy()
。返回主屏幕的操作会将您的应用置于后台,而不是完全关闭应用。
在调用 onPause()
后,该应用不会再获得焦点。在 onStop()
之后,该应用将不再显示在屏幕上。虽然该 activity 已停止,但 Activity
对象仍位于内存中(在后台)。该 activity 尚未销毁。用户可能会返回该应用,因此 Android 会保留您的 activity 资源。
- 使用“最近”屏幕返回该应用。请注意,在 Logcat 中,该 activity 使用
onRestart()
和onStart()
重启,然后使用onResume()
恢复。
当该 activity 返回前台时,系统不会再次调用 onCreate()
方法。相应 activity 对象未被销毁,因此不需要重新创建。系统会调用 onRestart()
方法,而不是 onCreate()
。请注意,这一次该 activity 返回前台时,系统会保留 Desserts Sold 数值。
- 除 DessertClicker 以外,至少启动一个应用,这样设备的“最近使用的应用”屏幕中就会有一些应用。
- 启动“最近使用的应用”屏幕,然后打开另外一个近期 activity。然后,返回最近用过的应用并让 DessertClicker 返回前台。
请注意,您在 Logcat 这里看到的回调与按主屏幕按钮后看到的相同。当应用进入后台时,系统会调用 onPause()
和 onStop()
,并在应用返回时调用 onRestart()
、onStart()
和 onResume()
。
此处的要点是,当用户导航到该 activity 或离开该 activity 时,系统会多次调用 onStart()
和 onStop()
。您应替换这些方法,使应用在进入后台后停止运行,或者在返回前台时重新启动。
onRestart()
又是什么情况呢?onRestart()
方法与 onCreate()
非常相似。无论是 onCreate()
还是 onRestart()
,都会在相应 activity 变得可见之前调用。onCreate()
方法只在第一次被调用,之后会调用 onRestart()
。onRestart()
方法用于放置仅在 activity 不是首次启动时才需要调用的代码。
用例 3:隐藏部分 activity
您已了解,在应用启动并且系统调用 onStart()
后,该应用将在屏幕上变得可见。在应用恢复并且系统调用 onResume()
后,该应用可获得用户焦点。应用在屏幕上完全显示并且具有用户焦点时的这部分生命周期称为“交互式”生命周期。
当应用进入后台后,焦点在 onPause()
后便会丢失,并且在 onStop()
后,该应用将不再可见。
焦点与可见性之间的区别非常重要,因为 activity 有可能是在屏幕上部分可见,但没有用户焦点。在这一步,您将查看 activity 处于部分可见状态,但没有用户焦点的情况。
- 在 DessertClicker 应用运行时,点击屏幕右上角的共享按钮。
共享 activity 出现在屏幕的下半部分,但相应 activity 在上半部分仍然可见。
- 检查 Logcat,您会注意到,系统仅调用了
onPause()
。
在此用例中,系统没有调用 onStop()
,因为相应 activity 仍然部分可见。但是,该 activity 没有用户焦点,并且用户无法与之交互。位于前台的“分享”activity 具有用户焦点。
为什么这种区别至关重要?以之前的计算密集型应用为例。您可能希望该应用在转入后台后停止运行,但在其被部分遮挡时继续运行。在这种情况下,您可以在 onStop()
中终止它。如果您希望该应用在相应 activity 被部分遮挡时也停止运行,则需要在 onPause()
中放置用于终止应用的代码。
在 onPause()
中运行的任何代码都会阻止其他内容显示,因此请使 onPause()
中的代码保持轻量级。例如,当有来电时,onPause()
中的代码可能会延迟来电通知。
- 在共享对话框之外点击一下,返回应用,您会注意到系统调用了
onResume()
。
onResume()
和 onPause()
都必须处理焦点。当相应 activity 具有焦点时,系统会调用 onResume()
方法;当该 activity 失去焦点时,系统会调用 onPause()
。
Android fragment 生命周期类似于 activity 生命周期,还具有几个特定于 fragment 的方法。
在此任务中,您将查看在之前的 Codelab 中构建的 AndroidTrivia 应用,并添加一些日志记录条目,以探索 fragment 生命周期。您可以在 AndroidTrivia 中回答与 Android 开发相关的问题;如果您连续答对三题,就会赢得游戏。
AndroidTrivia 应用中的每个屏幕都是一个 Fragment
。
为简单起见,您在此任务中使用 Android 日志记录 API,而不是 Timber 库。
- 从之前的一个 Codelab 中打开 AndroidTriviaNavigation 应用,或者从 GitHub 下载 AndroidTriviaNavigation 解决方案代码。
- 打开
TitleFragment.kt
文件。请注意,在您重新构建该应用之前,Android Studio 可能会显示绑定错误和未解析的引用错误。 - 向下滚动到
onCreateView()
方法。请注意,这里是膨胀 fragment 的布局和发生数据绑定的位置。 - 将日志记录语句添加到
onCreateView()
方法中,具体位置在setHasOptionsMenu()
代码行和要返回的最终调用之间:
setHasOptionsMenu(true)
Log.i("TitleFragment", "onCreateView called")
return binding.root
- 在
onCreateView()
方法的正下方,为每个剩余的 fragment 生命周期方法添加日志记录语句。代码如下:
override fun onAttach(context: Context) {
super.onAttach(context)
Log.i("TitleFragment", "onAttach called")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i("TitleFragment", "onCreate called")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.i("TitleFragment", "onViewCreated called")
}
override fun onStart() {
super.onStart()
Log.i("TitleFragment", "onStart called")
}
override fun onResume() {
super.onResume()
Log.i("TitleFragment", "onResume called")
}
override fun onPause() {
super.onPause()
Log.i("TitleFragment", "onPause called")
}
override fun onStop() {
super.onStop()
Log.i("TitleFragment", "onStop called")
}
override fun onDestroyView() {
super.onDestroyView()
Log.i("TitleFragment", "onDestroyView called")
}
override fun onDetach() {
super.onDetach()
Log.i("TitleFragment", "onDetach called")
}
- 编译并运行应用,然后打开 Logcat。
- 在搜索字段中输入
I/TitleFragment
,以过滤日志。该应用启动后,Logcat 将如下所示:
21933-21933/com.example.android.navigation I/TitleFragment: onAttach called 21933-21933/com.example.android.navigation I/TitleFragment: onCreate called 21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called 21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called 21933-21933/com.example.android.navigation I/TitleFragment: onStart called 21933-21933/com.example.android.navigation I/TitleFragment: onResume called
您可以在此处查看 fragment 的整个启动生命周期,包括以下回调:
onAttach()
:在 fragment 与其所有者 activity 相关联时调用。onCreate()
:与 activity 的onCreate()
类似,系统会调用 fragment 的onCreate()
来执行初始 fragment 创建(布局除外)。onCreateView()
:调用的目的是膨胀 fragment 的布局。onViewCreated()
:在onCreateView()
返回后立即调用,但要在任何已保存状态恢复到视图中之前。onStart()
:在 fragment 变为可见时调用;与 activity 的onStart()
并行。onResume()
:在 fragment 获得用户焦点时调用;与 activity 的onResume()
并行。
- 点按 Play 按钮继续玩知识问答游戏,观察现在的 Logcat。
21933-21933/com.example.android.navigation I/TitleFragment: onAttach called 21933-21933/com.example.android.navigation I/TitleFragment: onCreate called 21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called 21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called 21933-21933/com.example.android.navigation I/TitleFragment: onStart called 21933-21933/com.example.android.navigation I/TitleFragment: onResume called 21933-21933/com.example.android.navigation I/TitleFragment: onPause called 21933-21933/com.example.android.navigation I/TitleFragment: onStop called 21933-21933/com.example.android.navigation I/TitleFragment: onDestroyView called
打开下一个 fragment 会使标题 fragment 关闭,并调用以下生命周期方法:
onPause()
:在 fragment 失去用户焦点时调用;与 activity 的onPause()
并行。onStop()
:在 fragment 不再在屏幕上可见时调用;与 activity 的onStop()
并行。onDestroyView()
:在不再需要 fragment 的视图时调用,用于清理与该视图关联的资源。
- 在应用中,点按向上按钮(屏幕左上角的箭头),返回标题 fragment。
21933-21933/com.example.android.navigation I/TitleFragment: onPause called 21933-21933/com.example.android.navigation I/TitleFragment: onStop called 21933-21933/com.example.android.navigation I/TitleFragment: onDestroyView called 21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called 21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called 21933-21933/com.example.android.navigation I/TitleFragment: onStart called 21933-21933/com.example.android.navigation I/TitleFragment: onResume called
这次,系统可能不会通过调用 onAttach()
和 onCreate()
来启动 fragment。fragment 对象仍然存在并仍与其所有者 activity 相关联,因此生命周期以 onCreateView()
开头。
- 按设备的主屏幕按钮。请注意,在 Logcat 中,系统仅会调用
onPause()
和onStop()
。这与 activity 的行为相同:返回主屏幕会使 activity 和 fragment 置于后台。 - 使用“最近”屏幕返回该应用。与 activity 发生的情况一样,系统会调用
onStart()
和onResume()
方法,以将 fragment 返回到前台。
Android Studio 项目:DessertClickerLogs
activity 生命周期
- activity 生命周期是 activity 会切换的一组状态。 activity 生命周期在 activity 首次创建时开始,到 activity 被销毁时结束。
- 当用户在 activity 之间以及应用内外导航时,每个 activity 会在 activity 生命周期中的状态之间切换。
- activity 生命周期中的每种状态都有一个对应的回调方法,您可以在
Activity
类中替换此类方法。生命周期方法有七种:onCreate()
、onStart()
、onPause()
、onRestart()
、onResume()
、onStop()
和onDestroy()
- 如需添加要在 activity 转换为某种生命周期状态时发生的行为,请替换相应状态的回调方法。
- 如需在 Android Studio 中为类添加框架替换方法,请依次选择 Code > Override Methods,或按
Control+o
。
使用 Log 记录日志
- 借助 Android 日志记录 API(具体而言是
Log
类),您可以写入要在 Android Studio 内的 Logcat 中显示的简短消息。 - 使用
Log.i()
可写入信息性消息。此方法采用两个参数:日志标签(通常是类的名称)和日志消息(一个简短的字符串)。 - 使用 Android Studio 中的 Logcat 窗格可查看系统日志,包括您写入的消息。
使用 Timber 记录日志
Timber
是一个日志记录库,与 Android 日志记录 API 相比具有多项优势。具体而言,Timber
库具有下列优势:
- 可以根据类名称为您生成日志标签。
- 有助于避免在您的 Android 应用的发布版本中显示日志。
- 支持与崩溃报告库集成。
如需使用 Timber
,请将其依赖项添加到您的 Gradle 文件中,然后扩展 Application
类,以对其进行初始化:
Application
是一个基类,包含整个应用的全局应用状态。如果您未指定Application
类,Android 会使用默认的 Application 类。您可以创建自己的Application
子类来初始化Timber
等应用级库。- 通过将
android:name
属性添加到 Android 清单中的<application>
元素,可以将自定义Application
类添加到您的应用。请不要忘记执行此操作! - 使用
Timber.i()
可通过Timber
写入日志消息。此方法仅接受一个参数:要写入的消息。系统会自动添加日志标签(类名称)。
Android 开发者文档:
- activity(API 指南)
- fragment(API 指南)
Activity
(API 参考文档)Fragment
生命周期(API 参考文档)- 了解 activity 生命周期
- 使用 Logcat 写入和查看日志
Log
(API 参考文档)
其他:
- Timber (GitHub)
- Android 生命周期备忘单 - 第 I 部分:单个 activity 直观地回顾了这里的大多数资料。
- Android 生命周期备忘单 - 第 II 部分:多个 activity 显示了当两个 activity 进行交互时生命周期调用的顺序。