第 3 章 Activity 的生命周期

请参考教材,全面理解和完成本章节内容... ...

复制第二章的工程ch2目录,将工程目录改名为ch3。

每个Activity实例都有其生命周期。在其生命周期内,activity在启动、运行、暂停和停止四种状态间进行转换。每次状态发生转换时,都有一个Activity方法将状态改变的消息通知给activity。图3-1显示了activity的生命周期、状态以及状态切换时系统调用的方法。

image

图3-1 Activity的生命周期

仔细阅读图3-1有助于我们理解生命周期的调用和回调过程:

  • 在一个activity的生命周期中,系统会像金字塔模型一样去调用一系列的生命周期的回调方法。Activity生命周期的每一个阶段就像金字塔中的台阶。当系统创建了一个新的activity实例,每一个回调方法会向上一阶移动activity的状态。金字塔顶端意味着activity是跑在最前端的并且用户可以与它进行交互。
  • 当用户开始离开这个activity,为了卸载这个activity,系统会调用其它方法来向下一阶移动activity状态。在某些情况下,activity会隐藏在金字塔下等待(例如当用户切换到其他app),这个时候activity可以重新回到顶端(如果用户回到这个activity)并且恢复用户离开时的状态。

另外,周期中有三个状态是相对静态的,这三个状态下activity可以存在一段比较长的时间。(其它状态会很快就切换掉,停留的时间比较短暂)

  1. Resumed:在这个状态,activity是在最前端的,用户可以与它进行交互。(通常也被理解为"running" 状态)
  2. Paused:在这个状态,activity被另外一个activity所遮盖:另外的activity来到最前面,但是半透明的,不会覆盖整个屏幕。被暂停的activity不会再接受用户的输入且不会执行任何代码。
  3. Stopped:在这个状态, activity完全被隐藏,不被用户可见。可以认为是在后台。当stopped, activity实例与它的所有状态信息都会被保留,但是activity不能执行任何代码。

其它状态 (Created and Started)都是短暂的,系统快速的执行那些回调方法并通过执行下一阶段的回调方法移动到下一个状态。也就是说,在系统调用onCreate(), 之后会迅速调用onStart(), 之后再迅速执行onResume()。上面就是基本的activity生命周期。

这些回调方法非常有用,Activity的子类可以在activity的生命周期状态发生关键性转换时,通过覆盖回调方法完成某些工作。

我们已经熟悉了这些方法中的onCreate(Bundle)方法。在创建activity实例后,但在此实例出现在屏幕上以前,Android操作系统会调用该方法。

通常,activity通过覆盖onCreate()方法来准备以下用户界面相关的工作:

  • 实例化组件并将组件放置在屏幕上(调用方法setContentView(int));
  • 引用已实例化的组件;
  • 为组件设置监听器以处理用户交互;
  • 访问外部模型数据。

永远不要自己去调用onCreate()方法或任何其他Activity生命周期方法,记住这一点很重要。我们只需在activity子类里覆盖这些方法即可,Android会适时去调用它们。

提示:

所谓回调,就是对象A调用另一对象B中的某个方法f1,然后B又在某个时候反过来调用A中的某个方法f2,对于B来说,这个f2便叫做回调方法。

通常,回调方法是那些给别""调用的方法,自己不会主动执行。

例如:消息响应方法就可以看成是回调方法,因为是让系统在“有消息”时去调用。

3.1 日志跟踪理解 Activity 生命周期

通过覆盖activity生命周期方法的方式,来探索QuizActivity的生命周期。

3.1.1 输出日志信息

Android内部的android.util.log类能够发送日志信息到系统级别的共享日志中心。Log类有好几个日志信息记录方法。本书使用最多的是以下方法:

public static int d(String tag, String msg)

在QuizActivity.java中,为QuizActivity类新增一个名为MYTAG的常量,其值是类名"QuizActivity"如代码清单3-1所示。

代码清单3-1 新增一个MYTAG常量(QuizActivity.java)

image

然后,在onCreate()方法里调用 Log.d()方法记录日志信息,如代码清单3-2所示。

代码清单3-2 为onCreate()方法添加日志输出代码(QuizActivity.java)

image

参照代码清单3-2输入相应代码,Android Stuido可能会提示无法识别Log类的错误。这时,记得使用Alt+Enter组合键进行类包导入。在Android Stuido询问引入哪个类时,选择android.util.Log类。

接下来,在QuizActivity类中,继续覆盖其他五个生命周期方法,如代码清单3-3所示。

代码清单3-3 覆盖更多生命周期方法(QuizActivity.java)

image

3.1.2 使用 LogCat

日志在任何项目的开发过程中都会起到非常重要的作用,在 Android项目中如果你想要查看日志则必须要使用 LogCat 工具。

要想打开LogCat,可在 Android Studio 最下方的 Status Bar 中找到 6:Android,点击它。如图3-2所示。

image

图3-2 打开 LogCat

LogCat 出现了,信息量很大,当前我们只需要理解三个“选择”即可。图3-2中,三个蓝色标识含义如下:

  • 用来选择Devices,如果只连接了一个设备的话,不需要选择。
  • 用来选择 Log level,日志级别。包括:Verbose(全部信息)、Debug、Info、Warn、Error、Assert。以上级别依次升高。
  • 用来选择过滤器,可根据过滤条件显示符合条件的日志。

运行GeoQuiz应用, 你会发现急速翻滚的各类信息立即出现在LogCat窗口中。其中大部分信息都来自于系统的输出,如图3-3所示。

image

图3-3 LogCat中翻滚的各类信息

另外, 在Android视图中还包括,Memory、CPU和ADB Logs三个工具。应用运行时,可利用他们查看内存使用情况、CPU占用情况和ADB(调试工具)的调试日志,如图3-4所示。

image

图3-4 Memory、CPU和ADB Logs三个工具

为方便日志信息的查找,可使用TAG常量过滤日志输出。点击过滤器那个下拉框,选择 Edit Filter Configuration,在弹出的对话框中点击左上角的加号新创建一个 Filter。Filter Name(过滤器名称)输入QuizActivity, Log Tag同样输入QuizActivity,如图3-5所示。

image

图3-5 在LogCat中创建过滤器

过滤器各字段含义如下:

  • Name:过滤器名称
  • by Log Tag: 通过日志的 tag 过滤
  • by Log Message:通过日志的 msg 内容过滤
  • by Package Name:通过包名过滤
  • by PID:通过PID过滤
  • by Log Level:通过日志等级过滤
  • regex:表示可以使用正则表达式进行匹配

以上过滤条件可以组合。

我建了一个QuizActivity的过滤器,过滤条件是TAG等于 QuizActivity

然后重新运行,将 Filter 选择为 QuizActivity,我们的日志出现了。当然还可能会有一些奇奇怪怪的日志也出现了,每条Log中都包含有 QuizActivity

仔细观察,你会发现GeoQuiz应用启动并完成QuizActivity初始实例创建后,有三个生命周期方法被调用了, 如图3-6所示:

image

图3-6 应用启动后,被调用的三个生命周期方法

如看不到过滤后的信息列表,请选择LogCat左边窗口的QuizActivity过滤项,或者重新启动应用。

3.2 设备旋转与 Activity 生命周期

现在,我们来处理第2章结束时发现的应用缺陷。运行GeoQuiz应用,单击Next按钮显示第二道地理知识问题,然后旋转设备。(据Android手册:使用Control+F12/Ctrl+F12组合键可以让模拟器的旋转。但我一直没有成功L)

设备旋转后,GeoQuiz应用又重新显示回第一道问题。查看LogCat日志查找问题原因,如图3-10所示。

image

图3-10 QuizActivity已死,QuizActivity万岁

设备旋转时,当前看到的QuizActivity实例会被系统销毁,然后创建一个新的QuizActivity实例。再次旋转设备,查看该销毁与再创建的过程。

这就是问题产生的原因。每次创建新的QuizActivity实例时,mCurrentIndex会被初始化为0,因此用户又回到了第一道问题上,稍后我们会修正这个缺陷。

创建水平模式布局

在项目导航视图中,右键单击res目录, New->Director, 创建一个新文件夹并命名为layout-land,如图3-11所示。

image

图3-11 创建新文件夹

将activity_quiz.xml文件从res/layout/目录复制至res/layout-land/目录。现在我们有了一个水平模式布局以及一个默认布局(竖直模式)。注意,两个布局文件必须具有相同的文件名,这样它们才能以同一个资源ID被引用。

(有时需要将导航视图切换为Project 模式,才能看见新建的layout-land目录)

注:教材对目录 res\layout-land\ 的水平布局文件做了修改,但新版Android Studio对此“修改”支持的不好,我还没找到解决方法,暂不做不做修改。目录 res\layout\ 和目录 res\layout-land\ 的activity_quiz.xml文件内容相同。

3.3 设备旋转前保存数据

请参考教材,理解和完成本节内容... ...。

代码清单3-5 新增键?值对的键(QuizActivity.java)

image

代码清单3-6 覆盖onSaveInstanceState()方法(QuizActivity.java)

image

代码清单3-7 在onCreate()方法中检查存储的bundle信息(QuizActivity.java)

image

posted @ 2015-08-22 10:03  jlxuqiang  阅读(1165)  评论(0编辑  收藏  举报