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做相关开发,不要忘了向下兼容。

个人博客: IT老五
微信公众号:【IT老五(it-lao5)】,一起源创,一起学习!

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容