Android–四大组件之Activity(一)

1. Activity是什么?

2. 生命周期

  • 1). Activity跳转
  • 2). 从后台启动
  • 3). 横竖屏切换

3. 启动模式

  • 1). 任务栈
  • 2). laucherMode
  • 3). Intent的Flag
  • 4). startActivityForResult

Activity是什么?

Activity是Android四大组件之一,可以用于view的显示,但其最主要的任务是承担用户和app之间的交互。
在MVC模式中,Activity主要充当着C(Controller控制层)的作用,同时也负责Dialog,Toast,PopupWindow等;而在MVP模式中,Activity仅充当V(View视图)的角色。

生命周期

说到Activity,不可避免会谈到其生命周期,在一次正常的启动到销毁的过程中,Activity的生命周期包含6部分:

onCreate() –> onStart() –> onResume() –> onPause() –> onStop() –> onDestroy()

然后考虑到Activity进入后台重新进入会调用onRestart(),所以其生命周期为:

Activity生命周期

上图比较详细的描述了正常情况下Activity的生命周期切换。那下面我们看看一些特定情况下生命周期变化。

Activity跳转

从Activity A跳到Activity B时,生命周期是怎样的?
Activity A会有一个完整的启动过程,onCreate() –> onStart() –> onResume(),而且会在Activity B的onCreate()执行前就触发onPause(),而Activity A的onStop()会在Activity B执行完onResume()以后再执行。
此时两个Activity的生命周期顺序是(控制台log):

05-19 22:42:28.543 7534-7534/com.itlao5.demo I/System.out: ActivityA onCreate()
05-19 22:42:28.544 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:42:28.546 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()
05-19 22:42:51.360 7534-7534/com.itlao5.demo I/System.out: ActivityA onPause()
05-19 22:42:51.427 7534-7534/com.itlao5.demo I/System.out: ActivityB onCreate()
05-19 22:42:51.428 7534-7534/com.itlao5.demo I/System.out: ActivityB onStart()
05-19 22:42:51.430 7534-7534/com.itlao5.demo I/System.out: ActivityB onResume()
05-19 22:42:51.927 7534-7534/com.itlao5.demo I/System.out: ActivityA onStop()

当然,从Activity B返回Activity A时,生命周期的执行情况类似。

05-19 22:55:43.390 7534-7534/com.itlao5.demo I/System.out: ActivityB onPause()
05-19 22:55:43.411 7534-7534/com.itlao5.demo I/System.out: ActivityA onRestart()
05-19 22:55:43.413 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:55:43.420 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()
05-19 22:55:43.910 7534-7534/com.itlao5.demo I/System.out: ActivityB onStop()
05-19 22:55:43.911 7534-7534/com.itlao5.demo I/System.out: ActivityB onDestroy()

从后台启动

Activity处于前台时,点击home键,然后重新进入Activity(Activity没有销毁),此时不会再执行onCreate,而是会执行onRestart() –> onStart() –> onResume():

05-19 22:43:51.588 7534-7534/com.itlao5.demo I/System.out: ActivityA onPause()
05-19 22:43:51.654 7534-7534/com.itlao5.demo I/System.out: ActivityA onStop()
05-19 22:45:50.567 7534-7534/com.itlao5.demo I/System.out: ActivityA onRestart()
05-19 22:45:50.585 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:45:50.593 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()

而当在后台时,Activity已经被销毁了,则会重新进入onCreate()的创建过程,而不会执行onRestart()。

横竖屏切换

如果没有锁定Activity的横竖屏,则系统会根据手机横竖屏状况来调整Activity的横竖屏显示,此时,会有一些生命周期的变化。
此时的横竖屏切换会根据android:configChanges的设置,产生三种不同的结果:
1.当不设置android:configChanges时,横竖屏切换生命周期是这样的:

(Activity启动) onCreate –> onStart –> onResume –> (切横屏) onSaveInstanceState –> onPause –> onStop –> onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume –> (切竖屏) onSaveInstanceState –> onPause –> onStop –>
onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume –> onSaveInstanceState –> onPause –> onStop –> onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume

可以看到,当从横屏切换为竖屏时,Activity会有一个销毁到重新创建的过程,而从竖屏切换为横屏时,Activity会销毁及创建两次。

  1. 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。

(Activity启动) onCreate –> onStart –> onResume –> (切横屏) onSaveInstanceState –> onPause –> onStop –> onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume –> (切竖屏) onSaveInstanceState –> onPause –> onStop –>
onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume –> onConfigurationChanged

我们看到,虽然切换为竖屏时,生命周期只执行了一次,但是,在最后海执行了onConfigurationChanged。

  1. 改成 android:configChanges=”orientation|keyboardHidden”之后

(Activity启动) onCreate –> onStart –> onResume –> (切横屏) onConfigurationChanged –> (切竖屏) onConfigurationChanged –> onConfigurationChanged

此时,竖屏切换为横屏,仅执行一次onConfigurationChanged,而横屏切换为竖屏时,执行两次onConfigurationChanged。

需要注意的是:以上结果仅适用于targetSdkVersion<13时,而从Android 3.2(API 13)开始
  1. 以上1.2都会执行相同的操作,横竖屏切换的生命周期完全一致,都是

(Activity启动) onCreate –> onStart –> onResume –> (切横屏) onConfigurationChanged –onSaveInstanceState –> onPause –> onStop –> onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume –> (切竖屏) onConfigurationChanged — > onSaveInstanceState –> onPause –> onStop –> onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume

2.在设置Activity的android:configChanges=”orientation|keyboardHidden”后,还是与1一样会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。因此,阻止程序在横竖屏切换时重新加载Activity,除了设置”orientation”,还必须加上”ScreenSize”,即android:configChanges=”orientation|keyboardHidden|ScreenSize”。此时的生命周期,无论横竖屏切换都只执行onConfigurationChanged:

(Activity启动) onCreate –> onStart –> onResume –> (切横屏) onConfigurationChanged –> (切竖屏) onConfigurationChanged

启动模式

启动模式也是谈Activity时不可避免的,Activity有四种启动模式:

standard、singleTop、singleTask、singleInstance

可以根据不同的需求,来使用对应的启动模式,防止Activity的重复创建。
在谈到启动模式之前,我们先了解一下Activity的任务栈:

任务栈

任务栈,是一种用来保存Activity实例的容器,保存形式为栈(先进后出),它包含两个操作,压栈和出栈,不能直接更改栈里面已存在Activity的顺序,只能通过压栈和出栈来调整顺序。
当启动Application时,默认会创建一个栈,所有该应用新创建的Activity都默认保存在该栈。当栈处于前台时,即为前台任务栈;而当我们按Home键或者切换到其他应用时,栈即为后台任务栈。Android当前显示的是前台任务栈的Top Activity。

launchMode

当然,仅仅依靠着一个先进后出的栈,很难满足我们的需求,比如我们的栈存在Activity A B C D,我们需要进入Activity B,但是又不想重新创建,并且当我们点返回的时候希望回到Activity A,那么我们就不能按照简单的创建一个Activity,而是需要给Activity一些特权,这些特权就是通过设置启动模式来实现。我们可以通过AndroidManifest文件中的属性andorid:launchMode或者通过Intent的flag来设置Activity的启动模式。
上面了解了任务栈,接下来我们再来看看Activity的四种启动模式:

standard

默认模式,不需配置。在这个模式下,每次都会默认创建一个新的Activity实例。因此,在这种模式下,在一个栈中可以有多个相同的实例,也允许多个相同Activity叠加。
应用场景:绝大多数Activity
示例:栈中有Activity a b c d, 启动一个Activity c,则栈变为a b c d c

需要注意的是:当跨进程调用默认模式的Activity时,在Android 5.0(API 21)以前,会将Activity增加在栈顶部,即两个进程的Activity会加入同一个栈中;而在Android 5.0及以后版本中,会重新创建一个新的栈,把另一个进程的Activity加入到新栈中。
singleTop

栈顶复用模式,即当栈的顶部是需要开启的Activity时,则不重新创建,此时会执行Activity的onNewIntent()方法。这样就避免了栈顶部Activity实例的重复创建。
应用场景:从外部进入的时候(如从通知栏进入到应用的Activity),或者为了解决重复跳转问题(设置singleTop也可作为一个解决按钮快速点击导致重复打开Activity的方案)
示例:栈中有Activity a b c d, 启动一个Activity d,则栈仍然是 a b c d,而如果是启动一个Activity c,则栈变为a b c d c

注意:和standard一样,跨进程调用时,Android 5.0以后才新创建栈,并将Activity加入其中。
singleTask

栈内复用模式,如果栈中已经存在该Activity的实例,那么,当再次开启该Activity时,会直接使用该实例,执行其onNewIntent()方法。与singleTop的区别是,singleTask只要栈内存在,不管是否为栈顶Activity,都会复用,而且会清除栈中处于该Activity实例之上的所有Activity。
应用场景:应用首页(应用首页设置singleTask,可以保证从首页返回,可以顺利推出应用)
示例:栈中有Activity a b c d, 其中b是singleTask,则当启动一个Activity b时,栈变为是 a b。

需要注意的是:多个栈之前跳转时,当一个前台栈跳转到一个全是singleTask Activity的后台栈时,会将后台栈移入前台栈中。比如:后台栈有singleTask Activity1 — singleTask Activity2,前台栈有Activity3 — Activity4,如果从Activity4启动Activity2,则最终的结果是,整个栈变为前台栈,Activity3 — Activity4 — singleTask Activity1 — singleTask Activity2
singleInstance

单例模式,系统中只会存在唯一的实例,只要存在该Activity实例,则无论哪个Activity启动,都会使用该实例。
应用场景:系统手机来电页,或者提供给第三方使用的辅助类应用的公用Activity

Intent的Flag

作为启动模式的另一种设置方式,Intent提供了多种Flag,比如:
FLAG_ACTIVITY_NEW_TASK:开启一个新的任务栈来存放启动的Activity,一般在service中使用该Flag(因为service中并不存在Activity任务栈,所以需要新创建)。
FLAG_ACTIVITY_SINGLE_TOP:这个与singleTop一致。
FLAG_ACTIVITY_CLEAR_TOP:与singleTask效果相同。
FLAG_ACTIVITY_NO_HISTORY:设置该Flag,则当该Activity启动其他Activity后,该Activity就消失了,不会保留在Activity栈中。

startActivityForResult

除了startActivity,Android还提供了startActivityForResult,用于开启Activity后可以接收返回值。有时,我们会发现,在通过startActivityForResult启动一个Activity后,onActivityResult中接收不到Activity的返回值。此时,也许是受你的Activity启动模式影响。

在Android5.0以前,当Activity A开启另一个Activity B时
1、如果Activity B是singleTask或者singleInstance,则无论A是什么启动模式,onActivityResult无法接收返回值;
2、如果Activity A是singleInstance,无论B是什么样的启动模式,都无法接收到返回值。

以上问题在Android5.0以后得到了修复,无论A、B是何种启动模式,都可以得到返回值。这是因为ActivityStackSupervisor类中的startActivityUncheckedLocked方法在5.0中进行了修改。在5.0之前,当启动一个Activity时,系统将首先检查Activity的launchMode,如果为A页面设置为SingleInstance或者B页面设置为singleTask或者singleInstance,则会在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK标志,而如果含有FLAG_ACTIVITY_NEW_TASK标志的话,onActivityResult将会立即接收到一个cancle的信息,而5.0之后这个方法做了修改,修改之后即便启动的页面设置launchMode为singleTask或singleInstance,onActivityResult依旧可以正常工作,也就是说无论设置哪种启动方式,StartActivityForResult和onActivityResult()这一组合都是有效的。所以如果你目前正好基于5.0做相关开发,不要忘了向下兼容。

更多启动模式相关的内容可以参考 https://www.jianshu.com/p/2a9fcf3c11e4

未完待续,Activity其他相关介绍后续文章再写



发表评论

必填项已用*标注