以下内容为复习总结,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!

Android关于Activity知识点总结(一)生命周期与状态及状态保存

一、任务(task)和返回栈(back stack)概述

说到任务不得不说android应用程序是由若干个Activity组成,每个Activity都可以设定各自特定的功能,来与用户进行交互操作,本应用中Activity之间可以相互开启,并传递一些消息或者数据。当然也可以通过设置Intent的action来打开第三方的应用的Activity,而当finish掉这个(或多个)Activity(无论是本应用的还是第三方应用的)时,仍然可以回到上一个Activity。是因为android将这些Activity都存在于一个相同的任务(task)中。

任务(task):是用户在执行某些工作时与之交互的Activity的集合。它使用栈来管理这些Activity,而这个栈就成为返回栈(back stack)

返回栈的特点:后进先出,栈中的Activity永远不会被重新排列,只会从栈顶中压入或弹出。

二、任务和返回栈(back stack)工作流程

(一)单任务时:

当用户点击home主屏的应用程序图标时,这个应用的任务就会转移到前台,如果当前应用没有任何任务,则说明此应用最近还没有被使用过,这时将会为此应用创建一个新的任务,并将该应用的主Activity放入返回栈中作为根activity。

此时在当前Activity中打开另一个Activity时,新的Activity将被推到这个返回栈的栈顶,并获得焦点,之前的Activity仍然在返回栈中,但处于Stopped状态,此时系统将保留其用户界面的当前状态,当用户按下返回键或者手动调用finish,栈中最顶端的Activity会被从栈中移除,此时将之前的Activity重新置回栈顶位置,恢复之前状态,获得焦点。下图为按时间轴的方式显示了Activity在返回栈中的状态变化:

当用户连续按下返回键时,那么栈中的每一个Activity都将从栈顶一个一个弹出,以显示前一个Activity,知道最终返回到主屏幕。当所有的Activity都才栈中移除掉时,此时栈为空,那么对应的任务也就不再存在。

(二)、多任务时

任务除了可以被转移到前台之外,还可以将其转移到后台。当开启一个新的任务,或者按home键返回主屏幕时,之前的任务就会被转移到后台,当任务处于后台时,返回栈中的所有Activity都将进入停止Stopped状态,但这些Activity在栈中的顺序和状态不会改变,只是失去了焦点。

下图就是多任务前后台的展示:

用户可以对任意任务做前台与后台的切换,如上图任务B此时正在前台与用户交互,而任务A在后台处于停止状态,等待恢复。当用户通过多任务键切换回任务A,则任务A转移到前台,恢复之前状态,获得焦点,进行与客户交互的操作,而任务B转移到后台,处于停止状态等待恢复;如果当用户按home键回到主屏幕时,任务B也将转移到后台,等待恢复。

注意:后台可以同时运行多个后台任务。由于android的回收(GC)机制,如果用户同时运行多个后台任务,当内存紧张时,系统将会销毁后台的Activity,以回收内存资源,从而导致Activity状态丢失。

由于返回栈中Activity永远不会重新排列的特点,因此如果应用运行用户从多个入口打开指定Activity时,则会创建这个Activity的新实力,然后压入栈顶,而不是将之前栈中有的实例放到栈顶。因此默认的任务中Activity会被多次实例化,如下图所示:

如果你不希望在应用中同一个Activity被多次实例化,也是可以的,可以通过管理任务(下文)设置Activity的启动模式或Intent的flag来控制。

总结:任务和Activity的默认行为

  • 当Activity_A启动Activity_B时,Activity_A将会停止,但系统会保留其状态(如:滚动位置和输入框的文本等)。如果用户在处于Activity_B时按back按钮时,则Activity_A将恢复其状态,继续执行。
  • 如果用户按back返回键时,则当前Activity将会从返回栈中弹出并销毁。返回栈中的前一个Activity将恢复执行。销毁Activity时,系统不会保留Activity的状态。
  • 用户通过按home键离开任务时,当前Activity将停止并且其任务将进入后台,系统会保留任务中所有Activity的状态,如果用户通过选择开始任务启动器的图标来恢复任务,则任务将会出现在前台并恢复执行栈顶的Activity。
  • 即使来自其他任务,Activity也可以被多次实例化。

三、管理任务

(一)概述

Android系统管理任务和返回栈的方式,如上文介绍,把所有连续启动的Activity都放入一个相同的任务中,并通过一个具有“后进先出”特点的返回栈管理。这种方式在绝大多数情况是没有问题的,我们也无需关心任务中Activity是如何压入和弹出栈的。但是如果你不想使用这种默认的行为,想根据自己意愿去操作Activity,比如:当启动一个新的Activity时,你希望它可以存在于一个独立的任务当中,而不是在现有的任务中;或者,当启动一个Activity时,如果这个Activity已经存在于返回栈中,你希望把这个Activity直接移至栈顶,而不是再新建一个它的实例;再或者,你希望可以将返回栈中除了最底层根Activity之外的其他所有Activity都移除出栈,等等这些功能,你都可以通过设置manifest清单文件中元素的属性,或者在启动Activity时配置Intent的flag来实现。

  • 通过元素属性控制时,可以使用的属性如下:
  1. taskAffinity       此属性控制Activity的亲和力的,控制它依附于哪个任务

  2. launchMode     此属性就是控制Activity的启动模式的

  3. allowTaskReparenting       与1配合使用

  4. clearTaskOnLaunch          与5和6都是对返回栈内Activity移除与否的控制

  5. alwaysRetainTaskState

  6. finishOnTaskLaunch

  • 通过Intent的flag控制时,常用的flag值如下:(在启动模式中详细介绍)
  1. FLAG_ACTIVITY_NEW_TASK         与“singleTask”类似,但不完全一样
  2. FLAG_ACTIVITY_CLEAR_TOP       与“singleTop”效果一样
  3. FLAG_ACTIVITY_SINGLE_TOP      这个会将它之上的Activity移除出栈

此处官方给出警告:

Caution: Most apps should not interrupt the default behavior for activities and tasks. If you determine that it's necessary for your activity to modify the default behaviors, use caution and be sure to test the usability of the activity during launch and when navigating back to it from other activities and tasks with the Back button. Be sure to test for navigation behaviors that might conflict with the user's expected behavior.

译文:

警告:大多数应用都不得中断 Activity 和任务的默认行为: 如果确定您的 Activity 必须修改默认行为,当使用“返回”按钮从其他 Activity 和任务导航回到该 Activity 时,请务必要谨慎并确保在启动期间测试该 Activity 的可用性。请确保测试导航行为是否有可能与用户的预期行为冲突。

(二)启动模式

启动模式是允许你去定义如何将一个Activity实例和当前任务进行关联。你可以通过以下两种方式进行定义启动模式:

  • 使用清单文件

在清单文件manifest中使用的launchMode属性值,来指定Activity启动时如何与任务关联。

  • 使用Intent的flag

调用startActivity()方法时,可以在Intent中加入flag标记,来声明新的Activity如何(是否)与当前任务关联。

注:

  1. 在Intent中定义的方式优先级要高于在清单文件中定义的方式。比如:如果Activity_A启动Activity_B,Activity_B在清单文件中定义它应该与当前任务如何关联,并且Activity_A也在启动Activity_B时在Intent的flag中要求B该如何与当前任务如何关联,那么此时Intent定义的方式将覆盖掉清单文件中定义的的方式。
  2. 有些启动模式在清单文件manifest中可以定义,但在Intent中无法定义;同样,有些启动模式在Intent中可以定义,但在清单文件manifest中无法定义。即两个设置启动模式的方法中定义的方式并不完全一样。

使用清单manifest文件

通过对标签中android:launchMode="以下参数"属性设置,此属性共有四种参数可供选择:

  • “standard”(默认启动模式)

这是标准模式,也是系统默认的启动模式。如同上边介绍,即使不定义launchMode属性,系统也会自动使用这种模式。这种模式每次启动Activity时,不管返回栈有没有这个Activity的实例存在,都会创建新Activity的实例,并把它放入当前的任务中。这种启动模式中,Activityke可以被多次实例化,在返回栈中也会有多个这种Activity的实例。

下图为此模式下返回栈中实例

小结:此模式下Activity每次都会新建实例,走完整的生命周期,这样相对很消耗系统资源。

  • “singleTop”

此模式是栈顶复用模式,从名字可以看出当要启动此模式下的Activity时,如果此Activity已经在返回栈中,并且处于栈顶位置,那么启动此Activity时将不会重新创建该Activity的实例,而是直接复用栈顶的这个Activity;当然如果此Activity没有在返回栈栈顶,或者在返回栈中没有此Activity的实例,那么都将重新创建此Activity的实例,压入栈顶。

实现复用的方式:当Activity在栈顶时,开启此Activity,会调用此Activity的onNewIntent(Intent intent)将在文末介绍)方法,此方法会在onPause()方法之前调用,而不会再走onCreate-onStart-onResume这个生命周期。

注意:为某个 Activity 创建新实例时,用户可以按“返回”按钮返回到前一个 Activity。 但是,当 Activity 的现有实例处理新 Intent 时,则在新 Intent 到达 onNewIntent() 之前,用户无法按“返回”按钮返回到 Activity 的状态。

下图为Activity_B在此模式下返回栈中实例:

小结:此模式下的Activity在返回栈中也可以被多次实例化,前提条件是:这个Activity实例不在返回栈的栈顶时。因此返回栈中可以有多个此Activity的实例。

使用场景:这种模式一般运用在一个Activity被频繁推到栈顶的情况,比如IM(即时通讯)聊天,有很多消息过来了,不可能每点击一个消息就去新建一个Activity;新闻推送,也不可能每次点击推送消息,就去新建一个Activity

  • “singleTask”

此模式也可以叫栈内复用,此模式下返回栈中只有一个此Activity的实例。开启此模式下的Activity时,系统会首先会判断是否有其需要的返回栈,1、如果没有,那么就创建返回栈并创建该Activity的实例压入栈内;2、如果有需要的返回栈,但是栈内没有此Activity的实例,那么就创建此Activity的实例并压入栈内;3、如果有需要的返回栈,并且栈内有次Activity的实例,那么无论次Activity实在栈顶,还是在栈内其他位置,都将复用此实例,并将此Activity实例之上的其他Activity实例移除出栈,将此实例至于栈顶,此时会调用Activity的onNewIntent(Intent intent)方法。

如何判断是都有此Activity实例需要的返回栈呢:

(a)这里就要说到Activity的taskAffinity属性了,此属性是设置Activity和任务关联的,系统通过这个值判断是否是这个Activity需要的返回栈。Android系统会检测要启动的Activity的affinity和当前任务的affinity是否相同?如果相同,则是当前Activity需要的任务(即返回栈);如果不同,则会新建一个任务,并将该任务task的taskAffinity设置为此Activity的taskActivity。

 (b)同一个应用中所有Activity的affinity都是默认相同的,都是自己的包名;所以不同的程序间是Activity的affinity是不相同的,所以启动自己的此模式下的Activity时是不会新建任务的,而启动其他应用的此模式下的Activity时,需要新建一个任务。

下图为Activity_B和Activity_E在此模式下返回栈的情况:

小结:在中不设置taskAffinity这个属性,开启本应用中此模式的Activity,会在栈中查看是否有次Activity的实例?如果有,则不会新建此Activity的实例,而是将此Activity之上的其他Activity移除出返回栈,并将此Activity置于栈顶,然后调用onNewIntent(Intent intent)方法;如果返回栈中没有此Activity的实例,那么就创建此Activity的实例,并压入栈顶。如果开启的其他应用此模式的Activity,则新开启一个任务,然后创建这个Activity的实例,然后压入新任务的返回栈栈顶。

:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity_B。

  • “singleInstance”

这个模式也有称它是单例模式。此模式和“singleTask”模式有点相似,只不过此模式下的Activity会单独在一个任务task中,且这个任务的返回栈中只有唯一的一个此Activity的实例,系统不会再向这个任务的返回栈中添加其他Activity。

举个例子:Android系统内置的浏览器程序声明自己浏览网页的Activity始终应该在一个独立的任务当中打开,也就是通过在元素中设置"singleTask"启动模式来实现的。这意味着,当你的程序准备去打开Android内置浏览器的时候,新打开的Activity并不会放入到你当前的任务中,而是会启动一个新的任务。而如果浏览器程序在后台已经存在一个任务了,则会把这个任务切换到前台。

下图Activity_B为此模式下的返回栈:

小结:开启此模式下的Activity,总是会为之新建一个任务task,此任务中的返回栈中只存放唯一一个此Activity的实例。而此时任务将会移至前台显示,当点返回键时,还会返回之前的Activity;然而如图又打开Activity_C时,则创建Activity_C的实例,将其Activity_C的实例压入之前的任务返回栈中,并将之前的任务至于前台,显示栈顶的Activity_C,而此时点返回键时,返回的0返回栈中Activity_C的上一个Activity_A。

下图为官网给出多任务回退逻辑:

使用Intent的flag

使用方法就是在使用startActivity的时候构建Intent,对Intent加入一个flag来改变Activity与任务的关联模式。

  • FLAG_ACTIVITY_NEW_TASK

设置这个flag这会产生与 "singleTask"值相同的行为。

启动Activity时系统会查找此Activity与之关联的任务,如果找不到就新建一个任务,然后创建此Activity的实例并压入栈顶,然将此任务与此Activity关联;如果找到与之关联的任务,则查看返回栈中是否已存在此Activity的实例?如果不存在,就新建此Activity实例并压入栈顶;如果存在,则将此Activity之上的其他Activity移除出栈,将此Activity置于栈顶,调用此Activity的onNewIntent(Intent intent)方法。

这个flag的作用通常是模拟一种Launcher的行为,即列出一推可以启动的东西,但启动的每一个Activity都是在运行在自己独立的任务当中的。

  • FLAG_ACTIVITY_SINGLE_TOP

设置这种flag和在launchMode中指定"singleTop"模式所实现的效果是一样的。

如果启动的Activity实例在栈顶,则不新建实例,直接调用此Activity的onNewIntent(Intent intent)方法;不在栈顶就创建新的实例。

  • FLAG_ACTIVITY_CLEAR_TOP

设置这种flag的行为,在launchMode没有与之对应的属性,算是flag的特色吧。

前提:Activity_A通过startActivity()启动flag指定成此模式的Activity_B,它分为以下两种情况:

(a)当Activity_B在manifest中指定启动模式“standard“(或不做任何设置)并且Intent的没有FLAG_ACTIVITY_SINGLE_TOP:

此时启动Activity_B,会在返回栈中查找Activity_B的实例是否存在?如果不存在,则新建Activity_B的实例压入栈顶;如果存在,则将Activity_B与它之上的所有Activity全部销毁,然后新建Activity_B,放入栈顶。

(b)除以上情况外,无论当Activity_B在manifest中指定除哪种启动模式,和Intent的其他flag任意结合:

此时启动Activity_B,同样会在返回栈中查找Activity_B的实例是否存在?如果不存在,则新建Activity_B的实例压入栈顶;如果存在,则将Activity_B之上的所有Activity全部销毁,然后复用Activity_B,调用Activity_B的onNewIntent()方法。

  • 通常FLAG_ACTIVITY_CLEAR_TOP与FLAG_ACTIVITY_NEW_TASK结合使用:

一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。比如可以将一个后台运行的任务切换到前台,并把目标Activity之上的其它Activity全部关闭掉。这个功能在某些情况下非常有用,比如说从通知栏启动Activity的时候。

四、清单文件manifest中其他属性介绍

处理affinity(有将其翻译为关联)

affinity所表示的是一个Activity 更愿意依附于哪个任务。默认情况下,同一应用中的所有 Activity 彼此关联,也就是有相同的affinity值,即包名。 因此,默认情况下,同一应用中的所有 Activity 相同任务中,此任务和Activity的affinity都是包名。 不过,您可以修改每个Activity 的affinity值,更改Activity与任务的关联。 在不同应用中定义的 Activity 可以共享关联,或者可为在同一应用中定义的 Activity 分配不同的任务关联。

可以通过更改元素的taskAffinity属性来修改和任务的关联,此属性可以接受一个字符串,但字符串不可以为本应用的包名,因为系统会默认使用包名定义affinity值。

affinity主要有以下两种应用场景:

  • 当调用startActivity()方法来启动一个Activity时,默认是将它放入到当前的任务当中。但是,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是"singleTask"),系统就会尝试为这个Activity单独创建一个任务。
    但是规则并不是只有这么简单,系统会去检测要启动的这个Activity的affinity和当前任务的affinity是否相同,如果相同的话就会把它放入到现有任务当中,如果不同则会去创建一个新的任务。而同一个程序中所有Activity的affinity默认都是相同的,这也是前面为什么说,同一个应用程序中即使声明成"singleTask",也不会为这个Activity再去创建一个新的任务了。
  • 当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在任务的能力。具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。
    那还是举一个形象点的例子吧,比如有一个天气预报程序,它有一个Activity是专门用于显示天气信息的,这个Activity和该天气预报程序的所有其它Activity具体相同的affinity值,并且还将allowTaskReparenting属性设置成true了。这个时候,你自己的应用程序通过Intent去启动了这个用于显示天气信息的Activity,那么此时这个Activity应该是和你的应用程序是在同一个任务当中的。但是当把天气预报程序切换到前台的时候,这个Activity又会被转移到天气预报程序的任务当中,并显示出来,因为它们拥有相同的affinity值,并且将allowTaskReparenting属性设置成了true。

清空返回栈

如果用户将任务切换到后台之后过了很长一段时间,系统会将这个任务中除了最底层的那个Activity之外的其它所有Activity全部清除掉。当用户重新回到这个任务的时候,最底层的那个Activity将得到恢复。这个是系统默认的行为,因为既然过了这么长的一段时间,用户很有可能早就忘记了当时正在做什么,那么重新回到这个任务的时候,基本上应该是要去做点新的事情了。

当然,既然说是默认的行为,那就说明我们肯定是有办法来改变的,在元素中设置以下几种属性就可以改变系统这一默认行为:

  • alwaysRetainTaskState

如果将最底层的那个Activity的这个属性设置为true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。

  • clearTaskOnLaunch

如果将最底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将最底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。

  • finishOnTaskLaunch

这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。

五、onNewIntent(Intent intent)方法

  • 调用时机:

当开启设置了启动模式(“singleTop”、“singleTask”、“singleInstance”或设置Intent的flag)的Activity时,不会创建新的Activity实例,而是复用栈内已存在的实例,这时系统会让此Activity实例回调此方法,来重新启动它的Intent;

此方法在调用之前,Activity总是会被暂停或停止,因此onNewIntent()方法肯定会在onResume()方法之前被回调。具体要看Activity之前退到后台时Activity的状态,如果退到后台时处于暂停状态(即执行了onPause方法),此时重启Activity时会执行onNewIntent()-->onResume();如果退到后台时Activity处于停止状态(即执行了onStop方法),此时重启Activity则会执行onNewIntent()-->onRestart()-->onStart()-->onResume()。

  • 注:

此时getIntent()获得到的Intent仍然是之前老的Intent,如果你要获得新的Intent,你需要在onNewIntent()方法中调用setIntent(Intent intent)来更新。

参考官网文档:

https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack

更多相关文章

  1. No.11 使用firewall配置的防火墙策略的生效模式
  2. Android(OPhone) 学习笔记 - 记事本实例
  3. Activity 启动模式和 taskAffinity 属性详解
  4. android之Task和Back Stack(回退栈)
  5. Android中Activity启动模式详解,可以控制程序按home键后进来还会
  6. Android(安卓)画廊效果之ViewPager显示多个图片
  7. Android(安卓)NDK开发入门实例
  8. Android自动填写验证码
  9. android轻量级开源缓存框架——ASimpleCache(ACache)源码分析(封装

随机推荐

  1. Android中Listview的平滑滚动(smoothScro
  2. Android中 startService()和bindService(
  3. 如何修改三方构件的布局
  4. 走心的Android(安卓)10 适配指南,手把手上
  5. Android(安卓)4.0 与 android2.3 差异
  6. Android(安卓)联系人 数据库解析
  7. Android中获取TextView一行最多能显示几
  8. Android(安卓)P: U盘挂载过程简述
  9. Activity生命周期和对话框
  10. 《第一行代码》阅读笔记(三十四)——Materi