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 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会销毁及创建两次。
- 设置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。
- 改成 android:configChanges="orientation|keyboardHidden"之后
(Activity启动) onCreate --> onStart --> onResume --> (切横屏) onConfigurationChanged --> (切竖屏) onConfigurationChanged --> onConfigurationChanged
此时,竖屏切换为横屏,仅执行一次onConfigurationChanged,而横屏切换为竖屏时,执行两次onConfigurationChanged。
需要注意的是:以上结果仅适用于targetSdkVersion<13时,而从Android 3.2(API 13)开始
- 以上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做相关开发,不要忘了向下兼容。
个人博客: IT老五
微信公众号:【IT老五(it-lao5)】,一起源创,一起学习!
更多启动模式相关的内容可以参考 https://www.jianshu.com/p/2a9fcf3c11e4
未完待续,Activity其他相关介绍后续文章再写