第1章 Activtiy的生命周期和启动模式

1.1 Activtiy的生命周期全解

Activity的生命周期分为两部分内容,一部分是典型情形下的生命周期,另一个就是异常情形下的生命周期。所谓的典型情形下的生命周期是指在用户参与的情况下,Activity所经历的生命周期的改变;而异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生变化而导致的Activity被销毁重建。

1.1.1 典型情况下的生命周期分析

在正常情况下,Activity会经历如下生命周期。

(1)onCreate:表示的是Activity正在被创建,这是Activity生命周期中的第一个方法。在这个方法中我们可以做一些初始化工作,比如调用setContentView加载界面布局资源,初始化Activity所需数据。

(2)onRestart:表示的是Activity正在被重新启动。一般情况下,当当前的Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为所导致的,比如用户按Home键切换到桌面或者打开一个新的Activity,这时当前的Activity就会暂停,也就是onPause和onStop被执行了,接着用户又回到了这个Activity,就会出现这种情况。

(3)onStart:表示的是Activity正在被启动,即将开始,这时Activity已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为Activity已经显示出来了,但是我们看不到。

(4)onResume:表示的是Activity已经可见了,并且出现在前台也开始活动。要注意这个和onStart的对比,onStart和onReume都表示可见,但是onStart的时候Activity还在后台,onResume的时候才显示到前台。

(5)onPause:表示的是Activity正在停止,正常情况下,紧接着onStop就会被调用。在特殊情形下,如果这个时候再快速的回到当前的Activity,那么onResume就会被调用。onPause方法中不能执行太耗时的操作,因为会影响到新Activity的显示。都应该知道的是,旧Activity必须先执行完,新的Activity的onResume才会执行。

(6)onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。

(7)onDestory:表示的是Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里我们可以做一些回收工作和最终资源的释放。

正常情况下,Activity的常用生命周期就只有上面7个,下图更加详细的说明了Activity各种生命周期的切换过程。

针对上图,再附加一下具体说明,分为如下几种情况。

(1)一个Activity的第一次启动:onCreate->onStart->onResume

(2)当用户打开新的Activity或者切换到桌面的时候:onPause->onStop,这里有一种特殊情况,如果新Activity采用了透明主题,那么当前Activity不会回调onStop

(3)当用户再次回到原Activity时,回调如下:onRestart->onStart->onPause

(4)当用户按BACK键回退时,回调如下:onPause->onStop->onDestory

(5)当Activity被系统回收以后再次打开,生命周期方法回调过程和(1)一样,注意只是生命周期方法一样,不代表所有的过程一样

(6)从整个生命周期来看,onCreate和onDestory是配对的,分别标识着Activity的创建和销毁,并且只可能有一次调用。从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能会被调用多次;从Activity是否在前台的角度来看的话,onPause和onResume是配对的。这里注意一下,onStart和onStop是从Activity是否可见这个角度来回调的,而onPause和onResume是从Activity是否位于前台的角度类回调的,除了这种区别,在实际使用中没有其他明显区别。

1.1.2 异常情形下的生命周期分析

1,情况1:资源相关的系统配置发生改变导致的Activity被杀死并重新创建

例如:手机屏幕的横竖切换

当系统配置发生变化后,Activity会被销毁,其onPause,onStop,onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法是在onStop之前被调用的。需要强调的是这个方法只会出现在Activity被异常终止的情形下,正常情况下是不会调用这个方法的。当Activity被重新创建后,系统会调用onRestoreInstaceState【方法的的调用是在onStart之后】并且会把Activity被异常销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstaceState和onCreate方法。因此我们可以通过onCreate和onRestoreInstaceState方法来判断Activity是否被重建了。

如果重建了就可以取出之前保存在Bundle对象的数据来恢复。

这里注意一下:通过onCreate和onRestoreInstaceState方法都可以接收Bundle对象,但是这二者是有区别的。onRestoreInstaceState一旦被调用,其参数Bundle saveInstaceState是肯定有值的,我们不用额外的判空。但是onCreate不行,如果Activity是正常启动的话,参数Bundle saveInstaceState是null,所以必须进行判空操作。

同时,我们要知道,在onSaveInstanceState和onRestoreInstaceState方法中,系统自动为我们做了一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前的Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据,ListView滚动的位置等。这些View相关的状态系统都能默认为我们恢复。具体针对某一个特定的View系统能为我们恢复哪些数据,我们可以查看View的源码。和Activity一样,每个View都有onSaveInstanceState和onRestoreInstaceState这两个方法。

关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window会委托它上面的顶级容器去保存数据。顶级容器是一个ViewGroup一般来说它很可能是DecorView。最后顶级容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层,父容器委托子元素去去处理一件事情,这种思想在Android中很常见。例如View的绘制,事件分发等都是采用的这种思想。至于数据的恢复过程也是这样的就不再这里赘述了。

2,情况2:资源内存不足导致低优先级的Activity被杀死

Activity的优先级:

(1)前台Activity–正在和用户交互的Activity,优先级最高;

(2)可见但是非前台Activity–比如Activity中弹出了一个对话框,导致activity可见但是位于后台无法和用户直接交互;

(3)后台Activity–已经被暂停的Activity,比如执行了onStop,优先级最低;

当系统内存不足时,系统就会按照上述优先级杀死目标Activity所在的进程,并在后续通过在onSaveInstanceState和onRestoreInstaceState来存储和恢复数据。如果一个进程中没有四大组件那么这个进程就会很快被杀死。因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程是很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易的被系统杀死。

前面说了系统的配置发生变化的时候Activity是要被重建的。那么有没有方法不重建呢?答案是有的。系统配置中所包含的项目是非常多的,但是常用的就是locale(系统语言的切换),orientation(屏幕方向)和keyboardHidden(键盘的可访问性)这三个选项,其他很少使用。

我们所要做很简单就是在AndroidMenifest.xml中加入Activity的声明即可,代码如下:

<?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.test"
        android:versionCode="1"
        android:versionName="1.0">
      <uses-sdk android:minSdkVersion="8" />

      <application android:icon="@drawable/icon" android:label="@string/app_name">
          <activity android:name=".TestActivity"
                    android:label="@string/app_name"
                    android:configChanges="keyboardHidden|orientation">
              <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.LAUNCHER" />
              </intent-filter>
          </activity>

      </application>
  </manifest>

这样的话当系统配置发生变化的时候就不会重建Activity了。也不会调用onSaveInstanceState和onRestoreInstaceState方法来保存和恢复数据,取而代之的是系统调用了Activity的onConfigurationChanged方法,这个时候我们就可以做一些自己的特殊处理了。

1.2 Activity的启动模式

上一节我们讲了Activity在标准情况下和异常情况下的生命周期,这节我们还要深入到Activity的启动模式和标志位中去。

1.2.1 Activity的LaunchMode

(1)standard:标准模式,这也是系统的默认模式。在这种模式下,每次启动一个Activity就会新建一个实例,不管这个实例是否存在。被创建的实例的生命周期符合典型情况下的Activity的生命周期。一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了Activity那么这个Activity就运行在它的那个Activity所在的栈中。比如A启动了B,如果B是标准模式的话,那么B就会运行在A的任务栈中。这里作者说到了一种情况,我们可以注意一下。就是非Activity类型的context是没有任务栈的,所以用非Activity类型的context去启动standard模式额Activity是会出问题的。解决的办法就是为待启动的Activity添加FLAG_ACTIVITY_NEW_TASK标志位,这样启动的话就会为它创建一个新的任务栈,这个时候待启动的Activity实际上是以singleTask模式启动的。

(2)singleTop:栈定复用模式。在这个模式下,如果新Activity已经位于任务栈的栈顶那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以读取当前请求的信息。

(3)singleTask:栈内复用模式。这是一种单实例的模式。在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重建实例。具体一点,当一个具有singleTask模式的Activity请求启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在就会重建一个任务栈,然后创建A的实例把A放入栈中。如果存在A的任务栈,这时就会看A是否在栈中有实例存在,如果实例存在,那么系统就会把A之上的Activity实例弹出去,使A位于栈顶然后回调它的onNewIntent方法,如果实例不存在,就创建A的实例并压入栈中。

(4)singleInstace:单实例模式。这是一种加强的singleTask模式,她除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此种模式的Activity只能单独地位于一个任务栈中。

上面多次说道任务栈。先从一个参数说起,TaskAffinity,可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字。默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然这个也不绝对,我们也可以人为的设置【不能和包名一致】。任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换后台任务栈再次调到前台。

在这里我们讨论一个有意思的问题从而来加深我们对singleTask的 理解。

假设有3个Activity A B C;A是入口Activity,模式是standard,而B和C是singleTask模式,并且指定了它们相同的affinaty属性。现在来这样的启动顺序:A->B->C->A->B,最后连续按两次BACK键,那么情况如何?大家可以思考一下。

答案是回到了桌面。为什么呢?这里给出解释。

A启动B,而B是singleTask模式,所以会创建一个名字为指定的affinaty的任务栈然后创建B的实例放入新建的这个任务栈中,再由B启动C,由于C也是singleTask模式,但是它所需要的任务栈已经有了【B和C具有相同的affinaty属性】,那么就直接创建C的实例放入到B所在的任务栈顶就行了。后来C又启动了A,由于A是标准模式,那么就是谁启动它就在谁的栈中,换句话说A也放入到了B所在的栈顶了。现在,有两个栈,一个是名字为包名的默认任务栈,里面只有A,还有一个就是名字为指定的affinaty的任务栈,里面有BCA。接下来A再启动B,由于B是singleTask,B就需要回到任务栈的栈顶,那么AC就必须出栈。这时任务栈中就只有B了。接着按BACK,B就出栈了,这时B所在的任务栈就不存在了,这个时候只能是回到后台任务栈并把A显示出来。如果再按一下BACK的话,就回到桌面了。

1.2.2 Activity的FLAGS

大家应该都知道有两种办法指定Activity的启动模式,一种是在 配置文件中配置,还有一种就是在Intent中设置标志位。有如下的几种常见的标志位:

FLAG_ACTIVITY_NEW_TASK:指定singleTask模式

FLAG_ACTIVITY_SINGLE_TOP:指定singleTop模式

FALG_ACTIVITY_CLEAR_TOP:具有此标记的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity实例都要出栈。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回退到我们的Activity的时候这个标记比较有用。

1.3 IntentFilter的匹配规则

大家都知道,启动Activity时,隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity。IntentFilter中的过滤信息有action,category,data。

为了匹配过滤列表,需要同时匹配过滤列表中的action,category,data信息。否则会匹配失败。一个过滤列表中的action,category和data可以有多个,所有的action,category和data分别构成不同的类别,同一类别的信息共同约束当前类别的匹配过程,只有一个Intent同时匹配action类别,category类别和data类别才算完全匹配,只有完全匹配才会成功启动目标Activity。另外一点,一个Activity中可以有多个intent-filter,一个intent只要能完全匹配任何一组intent-filter即可成功启动对应的Activity。

下面简单的分析各种属性的匹配规则。

1.3.1 action的匹配规则

action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action。

Intent对action的测试方式:

1>Intent中只能含有1个action.

2>intent-filter中可以配置多个action.

3>如果Intent对象的action配置在intent-filter

action声明的范围之内, 那么action测试成功.

4>如果Intent对象中没有包含action,则默认匹配失败

1.3.2 category的匹配规则

category是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category。

1> intent对象中可以包含多个Category

2> intent-filter中可以声明多个category

3> 若intent对象中包含的所有category都和intent-filter

中声明的某一个category相同, 那么category

测试成功.

4> 若intent中不包含category,则可以通过所有

的category的测试.

5>如果要一个activity能够接收隐式调用的话,就必须在intent-filter中指定“android.intent.category.DEFAULT”【系统在启动Activity的时候默认会加上android.intent.category.DEFAULT”这个category】

1.3.3 data的匹配规则

data由两部分组成,mimeType和URI。mimeType指的是媒体类型,比如image/jpeg等,而URI中包含的数据就多了。

下面是URI的结构:

://:/[||]

例如:content://com.example.project:200/folder/subfolder/etc

http://www.baidu.com:80/search/info

Scheme:URI的模式,比如http,file,content等,如果URI中没有指定scheme,那么整个URI的其他参数无效。这也意味着URI是无效的。

Host:URI的主机名,比如www.baidu.com,如果Host未指定,那么整个URI的其他参数也是无效的,这也意味着URI是无效的。

Port:URI中的端口号,只有当URI中指定了Scheme和Host时Port才是有意义的。

介绍完了data的数据格式之后,我们再来看一看data的匹配规则。

Intent对Data的测试

1> intent对象中可以含有1个具体的Data(Uri)

2> intent-filter中定义当前activity可以处理的

数据的格式要求.

 <action
 android:name = "android.intent.action.MAIN"/>
 <category android:name="android.intent.category.LAUNCHER"/>

这二者的作用是用来表明这是一个入口Activity并且会出现在系统的应用列表中。

第一种情况:有MAIN,无LAUNCHER,程序列表中无图标

原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里

第二种情况:无MAIN,有LAUNCHER,程序列表中无图标

原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现

这一章大概就这么些东西了。我也是看了两遍才打算开始写笔记的。

时间: 2024-10-13 12:50:23

第1章 Activtiy的生命周期和启动模式的相关文章

第一章:Activity的生命周期和启动模式

Activity是Android中四大组件之首,所以需要重视,尤其是启动方式,在AndroidManifest.xml中的注册信息 (一)Activity的生命周期 1.1.1 正常情况下的生命周期 说明 (1)针对一个特定的Activity,第一次启动顺序:onCreate->onStart->onResume. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause->onStop (3)返回原Activity时,回调如下:onRestart->on

Android开发艺术探索——第一章:Activity的生命周期和启动模式

Android开发艺术探索--第一章:Activity的生命周期和启动模式 怀着无比崇敬的心情翻开了这本书,路漫漫其修远兮,程序人生,为自己加油! 一.序 作为这本书的第一章,主席还是把Activity搬上来了,也确实,和Activity打交道的次数基本上是最多的,而且他的内容和知识点也是很多的,非常值得我们优先把他掌握,Activity中文翻译过来就是"活动"的意思,但是主席觉得这样翻译有些生硬,直接翻译成"界面"可能更好,的确,Activity主要也是用于UI效

Android开发艺术探索(一)——Activity的生命周期和启动模式

Activity的生命周期和启动模式 生命周期有? 1.典型情况下的生命周期—>指有用户参与的情况下,Activity所经过的生命周期改变 2.异常情况下的生命周期—>指Activity被系统回收或者由于当前设备的configuration发生改变而导致Activity被销毁重建 1.1 典型生命周期分析 旧活动的OnPause先调用,新Activity才启动 异常分析: 1.资源相关的系统配置发生改变,导致Activity被杀死并重新创建 系统只在Activity导致终止的时候才会调用OnS

Activity生命周期以及启动模式对生命周期的影响

前天用户体验反馈的一个需求,要求每次进入应用都定位到首页;这个操作很明显不适合放在首页Activity(启动模式为SingleTask)的onResume中,如果对Activity的启动模式和生命周期熟悉,那么很容易想到在onNewIntent里操作就可以了,这样在应用内跳转不至于每次都定位到首页. 可见,对Activity生命周期和启动模式进行一下梳理很有必要.下面主要描述一下Activty的生命周期,以及launchMode对生命周期的影响,onNewIntent,onSaveInstanc

Activity生命周期和启动模式

Activity正常情况下的生命周期: 1.   一个Activity的正常启动: onCreate --> onStart --> onResume 2.   A(Activity)打开新的B(Activity)或者用户按住Home键切换到主界面,当前Activity所调用的方法:  onPause  -->  onStop 3.   此时在重新回到A(Activity)所调用的方法:  onRestart  -->  onStart  -->  onResume 4,  

Android Activity生命周期及启动模式

曾经搞过许多东西,再熟练的东西一段时间没有碰都会生疏或忘记.后来体会到写成文档记录下来的重要性,但有些word或ppt记录下来的东西随着时间流逝会丢失,或者不愿去看.或许保存成博客的形式,会是更好的选择,勉励自己. Activiy是Android开发中最常用最常见的组件,是Android开发人员开始学习首先接触的组件,也是大部分人建立一个工程后,首先要处理的可能就是Activity.这么重要.常用.简单.易上手的组件,牢固掌握其基础知识,对于开发人员是非常重要的.本文会首先解释其基本概念,对于其

Activity生命周期以及启动模式对生命周期的影响(二)

前面一篇文章概述了Android四大组件之一的Activity生命周期方法的调用先后顺序,但对于非标准启动模式下Activity被多次调用时的一些生命周期方法并未详细阐述,现在针对该情况着重记录. 现象 发布会demo中出现了这样的一种现象:当界面即将出现时,语音重复唤起该界面时,由于在onPause中调用了finish(),界面一直未显示出来,这不是我们想要的. 分析 由于系统组这边存在的一个bug,全屏的Activity出现时会带起在后台运行的应用界面,所以我们这边的Activity不得不采

Android Activity生命周期与启动模式

Activity的完整生命周期如下图: Activity的加载模式有四种: standard: 标准模式,默认的加载模式,每次通过这种模式启动目标Acitivity,都创建一个新的实例,并将该Activity添加到当前栈中. singleTop: 与标准模式类似,只有当Activity位于Task顶时,系统不会重新创建目标Activity的示例,而是直接复用已有的Activity实例. singleTask: 如果要启动的Activity不存在,系统创建Activity实例,并将它加入栈顶 如果

安卓开发 activity的生命周期以及启动模式

Activity生命周期 安卓活动由一个返回栈管理 安卓活动有四个状态 1.运行状态 当一个活动位于栈顶的时候,这个活动就处于运行状态,也就是和用户进行交互的那个界面. 2.暂停状态 当活动不处于栈顶,但依然可见.意思就是这个活动没有被完全覆盖,上面有一层对话框之类的. 3.停止状态 活动不处于栈顶,完全不可见.这个好理解吧,就是用户看不到了. 4.销毁状态 活动从栈中移除了,也就是被用户关闭了. Activity共有七个回调方法,覆盖了活动整个生命周期 1.onCreate() 活动创建时调用