Activity作为Android四大组件中最常用的组件,对其进行深入的学习是一个初级Android开发人员成长路上的必修课。
本篇文章主要针对Activity的生命周期、异常状态恢复、启动模式以及Intent隐式启动做相应的深入学习,挖掘Activity的各项进阶知识。
1.Activity的生命周期
先上一张官方的Activity生命周期图
接下来我们对Activity生命周期中的各状态进行逐一分析:
(1)onCreate:该方法只在Activity第一次被创建的时候调用。我们常常在其中进行一些初始化的操作,如setContentView、绑定事件、初始化各类数据等。
(2)onStart:该方法表示Activity正在启动。一般发生在Activity由不可见转变为可见时被调用。(此时的Activity已经可见了,但还没出现在前台,无法进行交互)
(3)onResume:该方法表示Activity不仅已经可见,而且此刻位于前台,可以与用户进行交互。此时该Activity一定位于当前返回栈的栈顶,且处于运行状态。
(4)onPause:该方法表示Activity正在停止,一般情况下onStop方法会紧接着被调用。通常我们会在这个方法中去做一些储存数据、释放资源的操作。此时该Activity将不再位于前台。注意的是,该方法中切不可执行耗时操作,否则会影响到新Activity的显示。
(5)onStop:该方法在活动完全不可见时被调用,可以在该方法内执行一些稍微重量级的回收工作,同样不可太耗时。
(6)onDestroy:该方法表示Activity即将被销毁,这是Activity生命周期中的最后一步,应该在该方法内执行最终的回收工作和资源释放。
(7)onRestart:该方法在Activity由停止状态转为运行状态的过程中被调用,一般发生在返回到上一个Activity这种情况下。
上述7个生命周期中的回调方法中,除了OnRestart方法,其他方法都是两两相对的,我们可以从下面几种角度对它们进行加深理解:
从Activity创建和销毁的角度来看,分别对应着OnCreate和OnDestroy;
从Activity是否可见的角度来看,分别对应着OnStart和OnStop;
从Activity是否位于前台的角度来看,分别对应着OnResume和OnPause。
下面列举几种情况下Activity的执行流程:
①Activity启动:onCreate->onStart->onResume
②打开新Activity或切换到桌面:onPause->onStop(特殊情况,当新Activity采用透明主题时,当前Activity不会回调onStop)
③回到原Activity:onRestart->onStart->onResume
④按back键回退:onPause->onStop->onDestroy
特别说明:
当启动一个新Activity时,一定是先执行当前Activity的onPause方法后,才会启动新的Activity,最后执行当前Activity的onStop方法。(所以onPause方法中一定不能执行耗时操作)
2.Activity异常状态下的生命周期
当资源相关的系统配置发生改变或是系统内存不足时,Activity可能会被杀死。由于Activity是被异常销毁掉的,所以用户的一些临时数据需要进行保存,以使该Activity重新创建时能复现销毁时的内容,如EditText中的内容等。
由系统内存不足所涉及到的进程优先级的问题,参考Android的进程优先级。
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestroy均会被调用,同时由于Activity是在异常情况下被终止的,所以系统会调用onSaveInstanceState方法来保存当前Activity的状态。该方法的调用时机在onStop之前,但和onPause没有绝对的时序关系。
当Activity被重新创建之后,系统会回调onRestoreInstanceState方法,并把Activity销毁时在onSaveInstanceState方法中所保存的Bundle作为参数传递到onCreate和onRestoreInstanceState方法中。从时序上来说,onRestoreInstanceState调用时机在onStart之后。
onRestoreInstanceState和onCreate虽然都能获得异常时传入Bundle参数,但是二者还是有一定的区别:onRestoreInstanceState方法一旦被调用,其Bundle参数一定是有值的;而onCreate方法在正常启动的时候,其Bundle参数为null,所以必须进行额外的判断才行。官方推荐使用onRestoreInstanceState方法进行数据恢复。
多次试验后的总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
在前4种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
当该activity重新位于前台时,并不会执行onRestoreInstanceState方法。
第五种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
当然,当系统配置发生变化时,Activity也能不被重建。通过给Activity指定configChanges属性可以实现这一目标,如不想让Activity在屏幕旋转时重建,就可以给configChanges属性添加orientation这个值。
android:configChanges="orientation"
通常我们用的只有local(系统语言)、orientation(屏幕方向)和keyboardHidden(调出键盘)这三个选项。
3.Activity的启动模式
系统会将启动的Activity放入ActivityTask中,该任务栈遵循后进先出的规则。当然,Android中还能通过指定lauchmode的方式来个性化activity在栈中的启动模式。
Activity有四种启动模式standard、singleTop、singleTask和singleInstance。
①standard:activity的默认启动模式。每次启动一个Activity的时候都会重新创建一个新的实例压入栈顶,不管当前栈中是否已存在该Activity。
②singleTop:栈顶复用模式。如果当前任务栈的栈顶是该Activity,那么此Activity不会被重新创建,同时会调用栈顶Activity的onNewIntent方法,通过此方法获取当前的请求信息。此时该Activity的onCreate、onStart不会被系统调用,仅执行onPause -> onNewIntent -> onResume;如果新Activity的实例在任务栈中已存在但并未位于栈顶,那么新Activity则依旧会重新创建。
③singleTask:栈内复用模式。这是一种单实例模式,该模式下,只要当前任务栈内存在即将要启动的Activity(不管在栈内何处),那么当前栈中该Activity上面的所有Activity都将出栈,并且该Activity将位于栈顶。和singleTop一样,singleTask也会调用该Activity的onNewIntent方法。(注意:singleTask模式下可以指定该Activity启动所需要的任务栈,若指定任务栈不存在,则会新建对应的任务栈后再把该Activity新建的实例放进去)。
④singleInstance:单实例模式。指定为singleInstance的Activity会启用一个新的任务栈来管理自身。只要该Activity未销毁,由于栈内复用的特性,后续的请求不会再创建新的任务栈,会一直复用处于该任务栈内的Activity。
特殊情况:TaskAffinity
上面提到在singleTask模式下,Activity需要选择自己所指定的任务栈,那么这个指定的任务栈又是在哪实现的呢?
答案就在TaskAffinity这个属性上。该参数能标识Activity所需任务栈的名字,默认情况下所有Acitivity的任务栈都为应用的包名。TaskAffinity属性的值为字符串,且中间必须包含包名分隔符“.”。TaskAffinity属性主要和singleTask模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
当TaskAffinity和singleTask模式配对使用时,待启动的Activity会运行在TaskAffinity指定的任务栈中。
当TaskAffinity和allowTaskReparenting配对使用时,情况较为复杂。例如有两个应用A和B,A启动了B中的Activity并且该Activity设置的allowTaskReparenting属性为true,那么当用户按home建回到桌面后再点击应用B图标,则不是启动了B的Launch Activity,而是重新显示的之前被A所启动的那个Activity。
除了通过launchmode来指定Activity的启动模式,还能通过Intent来动态指定Activity的启动模式。其中,Intent动态指定的优先级高于在xml中的静态注册方式。
动态指定主要是通过往Intent中添加Flag实现,如
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
以下列举几个常见的标记位,稍作理解即可。
FLAG_ACTIVITY_NEW_TASK:和在xml中指定singleTask模式相同
FLAG_ACTIVITY_SINGLE_TOP:和在xml中指定singleTop模式相同
FLAG_ACTIVITY_CLEAR_TOP:启动该Activity时,与其同一任务栈中所有位于它上面的Activity都要出栈。如果被启动的Activity采用standard模式,那么连同它之上的Activity都要出栈,系统再创建新的Activity实例放入栈顶。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记的Activity不会出现在历史Activity列表中。等同于在xml中指定Activity属性android:excludeFromRencents="true"
。
4.IntentFilter
IntentFilter主要作用于Intent的隐式调用中。IntentFilter为Intent的隐式调用设置了过滤规则,如果不匹配将无法成功启动目标Activity。IntentFilter中过滤的信息有action、category、data。
一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter就可以成功启动对应的Activity。
①action:action的匹配要求是Intent中的action存在且必须和过滤规则中的其中一个action相同,这里的相同指的是Intent中的action必须能够和过滤规则中的action的字符串的值完全一样。action区分大小写。
②category:Intent中如果含有category,那么所有出现的category都必须和过滤规则中的其中一个category相同。(为了使Activity能接受到隐式调用,必须在intent-filter中指定"android.intent.category.DEFAULT"这个category,以免在Intent未指定category时添加的默认值无法匹配)
③data:data和action的匹配原则类似。由于data的结构过于复杂且平时较少涉及到,故此处不展开学习。
在action和category中,有一类action和category比较特别:
它们的共同作用标明了这是一个入口Activity,二者缺一不可。
参考文献:
《第一行代码——Android(第2版)》
《Android开发艺术探索》
《深入理解Android内核设计思想》
若您觉得本文章对您有用,请您为我点上一颗小心心以表支持。感谢!