Activity
一、什么是Activity
官方解释:
The Activity class is a crucial component of an Android app, and the way activities are launched and put together is a fundamental part of the platform’s application model. Unlike programming paradigms in which apps are launched with a main()method, the Android system initiates code in an Activity instance by invoking specific callback methods that correspond to specific stages of its lifecycle.
Activity是Android应用的重要组成部分,它的启动和组合方式是Android应用程序模型的一个基本部分。与使用 main ()方法启动应用程序的编程范例不同,Android 系统通过调用与其生命周期的特定阶段相对应的特定回调方法来启动 Activity 实例中的代码。
我们在日常的开发中接触最多的可能就是Activity了,对于Activity我的理解是它作为Android四大组件之一,主要是给我们提供界面的展示和用户交互。这里要说下Android的四大组件,Activity、Service、Brocast、ContentProvider功能各不相同但是称它们为四大组件主要是它们四个是构成Android APP所必需的同时它们也都是Android应用的入口。我们都知道Java是采用main ()方法启动应用程序的,但是Android并没有采用这种方式,Android设计了四个组件并以这些组件为入口来启动一个Android应用,这些组件构成Android应用且把组件和进程进行剥离。这样其他的程序在启动该程序时只需启动需要的activity而并不需要把整个app当做整体启动起来。
二、生命周期
生命周期作为Activity老生常谈的知识点是我们必须要熟练掌握的。这里要分两种情况去理解掌握:正常情况下的生命周期和异常情况下的生命周期。
对于每个生命周期除了覆写回调方法还可以使用生命周期监听组件来实现在不同生命周期执行不同的任务(如Lifecycle组件)
正常情况下的生命周期
这可以说是Activity最基本的知识点了
- oncreate Activity创建回调
- onstart 在前台可见
- onresume 可见且可交互
- onpause 可见不可交互 不建议进行数据保存
- onstop 不可见 建议进行一些数据的保存(cpu密集型的数据保存)stop时activity仍在内存中当此时重新返回前台,那么系统会自动恢复之前UI状态。
- ondestory 销毁 要记得释放资源以及相关UI状态
下面是常见情况下Activity生命周期的回调
情况 | 回调 |
---|---|
第一次启动 | onCreate 、onStart、onResume |
从 A 跳转到不透明的 B | A_onPause、B_onCreate、B_onStart、B_onResume、A_onStop |
从 A 跳转到透明的 B | A_onPause、B_onCreate、B_onStart、B_onResume |
从不透明的 B 再次回到 A | B_onPause、A_onRestart、A_onStart、A_onResume、B_onStop、B_onDestory |
从透明的 B 再次回到 A | B_onPause、A_onResume、B_onStop、B_onDestory |
用户按 home 键 | onPause、onStop |
按 home 键回到应用 | onRestart、onStart、onResume |
用户按 back 键退出 | onPause、onStop、onDestory |
在7.0之后的多窗口模式下生命周期并未发生变化,只有跟用户交互的那个activity才是resumed状态。在全屏模式下切换到多窗口模式时生命周期变化跟从横屏切换到竖屏一致还有就是需要注意多窗口模式下的onpause和onstop的处理,比如视频的播放与暂停 需要在onstart中播放在onstop中暂停而不是onpause
再次补充一个关于生命周期的问题就是处于哪些生命周期时是可以被杀死的。
onCreate、onStart()、onRestart()、onResume()当Activity处于这些生命周期时是不可被kill的。onPause()在Honeycomb(3.X版本)之前是可以被kill的,而从Honeycomb开始系统在Activity回调onStop()
之前是不会杀死Activity的,因为这样可以确保在异常情况下onsaveinstancestate被调用,保存状态。
(Android P开始onsaveinstancestate在onStop()之后被调用)
注意此处不可被Kill指的的是不会被系统杀死,自己在app内主动finish或System.exit是可以kill掉当前activity的,finish是结束当前Activity不会杀死进程,System.exit是直接退出进程。
异常情况下的生命周期
我们主要注意以下两种情况下的生命周期:
系统配置发生变化
比如常见的如屏幕方向发生变化,配置发生变化是会导致Activity销毁重建的,如果我们想保持之前的Activity不被销毁那么我们可以在manifest中设置对应的配置信息 ,之后当其变化时会触发onConfigurationChanged回调此时是不会销毁重建的。但是要注意的是触发回调时当前组件(Activity)必须还在运行 如果组件被暂停 那么是不会触发回调的 。
内存紧张导致Activity被杀死
因为Android总体资源有限当内存紧张时系统会根据一定的优先级杀死一些Activity。这里的优先级依次是:前台Activity>可见Activity>后台Activity>空进程。
除此之外我们还要了解onsaveinstancestate/onrestoreinstancestate回调。
onsaveinstancestate是Activity因为异常被系统kill时用来保存当前Activity的有关状态和数据的,我们也可以在该回调中保存我们想要保存的数据以防止Activity因为异常被杀死而丢失数据。之后系统会在合适的时机重建该Activity此时就会触发onrestoreinstancestate回调,在该回调里我们可以拿到之前保存的状态和数据进行恢复(在oncreate中也可以拿到保存的数据不过需要进行判空,所以还是推荐在onrestoreinstancestate进行恢复)。此外保存 View 状态有两个前提:View 的子类必须实现了 onSaveInstanceState; 必须要设定 Id, 这个 ID 作为 Bundle 的 Key
最后关于这两个函数的回调时机,系统版本不同会有一些差异:
1、 api < 11,onSaveInstance在onPause之前执行
2、11 <= api < 28,onSaveInstance在onPause之后,onStop之前执行
3、api >= 28,onSaveInstance在onStop之后执行
补充几个跟生命周期有关的问题
-
activity A 跳转到activity B 两者经历的生命周期
onpasue(A) —>oncreate(B) —>onstart (B) —>onresume(B) —>onstop(A) —> onSaveInstanceState(A) -
问题1中B返回 A,两者经历的生命周期
onpause(B) —>onrestart (A) —>onstart(A) —> onresume(A) —>onstop(B) —> ondestory(B) -
两个 Activity之间跳转时必然会执行的是哪几个方法?
前一个 Activity 的 onPause,后一个 Activity 的 onResume -
前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出 Dialog,生命值周 期回调方法。
1 )前台切换到后台,会执行 onPause->onStop ,再回到前台,会执行 onRestart->onStart->onResume
2) 弹出 Dialog,并不会影响 Activity 生命周期 -
透明主题的 Activity 对生命周期的影响?
不会进入onstop -
锁屏与解锁屏幕,Activity 如何执行生命周期
锁屏时会执行
onPause()
和onStop()
, 而开屏时则应该执行onStart()
onResume()
Activity的三种运行状态
①Resumed(活动状态)
又叫Running状态,这个Activity正在屏幕上显示,并且有用户焦点。这个很好理解,就是用户正在操作的那个界面。
②Paused(暂停状态)
这是一个比较不常见的状态。这个Activity在屏幕上是可见的,但是并不是在屏幕最前端的那个Activity。比如有另一个非全屏或者透明的Activity是Resumed状态,没有完全遮盖这个Activity。
③Stopped(停止状态)
当Activity完全不可见时,此时Activity还在后台运行,仍然在内存中保留Activity的状态,并不是完全销毁。这个也很好理解,当跳转的另外一个界面,之前的界面还在后台,按回退按钮还会恢复原来的状态,大部分软件在打开的时候,直接按Home键,并不会关闭它,此时的Activity就是Stopped状态。
三、启动方式
分类一
显示启动
指明ComponentName有明确的启动目标
隐式启动
未指明ComponentName,通过匹配intentfilter找到可以启动的目标
分类二
不带返回值
通过startActivity
带返回值
startActivityForResult启动并在onActivityResult中接收返回的结果。
注意:
当activity A启动activity B时,新旧activity的启动并不是旧的销毁后新的才启动的,而是同步进行的但是也存在一定顺序,只有旧的activity onpause之后新的activity才会走oncreate 、onstart、onresunme等生命周期,然后旧的activity才会调用onstop
四、任务栈
Task是用户在执行某项任务时与之交互的Activity的集合。按照每个Activity打开的顺序排列在一个堆栈(先进后出)。一般来说处在栈顶的Activity是正在前台的Activity。
大多数任务都从设备主屏幕上启动。当用户轻触应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务就会转到前台运行。如果该应用没有任务存在(应用最近没有使用过),则会创建一个新的任务,并且该应用的“主”Activity 将会作为堆栈的根 Activity 打开。
总结Activity和Task的默认行为:
- 当Activity a 启动Activity b 时,Activity a 进入stop转态,但系统保持其状态(如滚动位置和输入到表单中的文本)。 如果用户按下Back键回来那么a 恢复其状态,b被弹出Task并被销毁。
- 当用户通过按下Home button将当前Activity停止并且它的任务进入后台。 系统保留任务中每个Activity的状态。 如果用户稍后通过选择启动该Task的启动图标恢复该任务,则该Task将到达前台并恢复堆栈顶部的Activity。
- Activity可以被多次实例化,同时可以从其他Task中实例化
一般而言我们不需要干预Task的默认行为,但是我们也可以通过以下方法去干预:
taskAffinity
Affinity表示Activity希望属于哪个Task。 默认情况下,来自同一个应用程序的所有Activity彼此都有相同的Affinity。可以设置Taskaffinity 属性来自定义Affinity。task自身的affinity取决于根Activity的affinity值,同一个task中的所有Activity具有相同的affinity。
affinity在什么场合应用呢?
1.根据affinity重新为Activity选择宿主task(与allowTaskReparenting属性配合工作);
2.启动一个Activity过程中Intent使用了FLAG_ACTIVITY_NEW_TASK标记,根据affinity查找或创建一个新的具有对应affinity的task
taskAffinity还有两点要注意:
1、根activity的taskAffinity可以决定task的“名字”,activity在启动时和re-parent时需要根据taskAffinity来确定该activity会出现在哪个task
2、优先级是activity中指定的taskAffinity>application中指定的taskAffinity>默认的包名
taskAffinity 属性主要和 singleTask 启动模式和 allowTaskReparenting 属性配对使用,在其他情况下没有意义。
launchMode
Android提供了四种启动模式:
- standard
默认模式 该模式下每次启动activity都会创建新的activity实例。特殊情况,如果在Service或Application中启动一个Activity,其并没有所谓的任务栈,可以使用标记位Flag来解决。解决办法:为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,创建一个新栈。 - singleTop
启动一个activity如果该activity实例已经已在栈顶则复用并回调onnewintent。应用场景 推送跳转activity - singleTask
启动一个activity时会先找自己所属的Task,如果该Task已经存在那么查看Task中是否有该activity实例 有则复用并回调onnewintent
如果所属的Task还未创建那么就先创建Task然后再创建Activity实例并入栈 所以说singleTask 启动模式的activity 是全局单例的。应用场景 app主页 - singleInstance
可以看做是singleTask的加强版,该启动模式下每次都会启动一个新的Task并将activity实例放到Task 并且Task中只有这一个activity实例。应用场景 呼叫来电、闹钟。
activity attributes或者intent flags
-
android:allowTaskReparenting
这个属性用来标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个task移动到有共同affinity的task,“true”表示可以移动,“false”表示它必须呆在当前应用的task中,默认值为false 重新宿主的操作发生在应用退后台再次重启过程中 -
android:alwaysRetainTaskState
如果用户长时间离开Task,系统将清除除根Activity以外的所有Activity。 当用户再次返回Task时,只恢复根Activity。但是设置该属性为true后就不会发生清除,即使在很长一段时间之后,任务仍然保留其堆栈中的所有活动。 -
android:clearTaskOnLaunch
在Task的根Activity中,只要用户离开Task并返回到该Task,堆栈就会被清除到根Activity。用户总是返回到Task的初始状态,即使离开Task只有一会儿。 -
android: finishOnTaskLaunch
它作用于一个单一的Activity,而不是一个完整的Task。 它会导致任何Activity消失,包括根Activity。用户离开,然后返回到Task,则该Task不再存在。 -
FLAG_ACTIVITY_NEW_TASK
其效果与指定Activity为singleTask模式一致。系统会寻找或创建一个新的task来放置目标Activity,寻找时依据目标Activity的taskAffinity属性进行匹配,如果找到一个task的taskAffinity与之相同,就将目标Activity压入此task中,如果查找无果,则创建一个新的task,并将该task的taskAffinity设置为目标Activity的taskActivity,将目标Activity放置于此task -
FLAG_ACTIVITY_SINGLE_TOP
其效果与指定Activity为singleTop模式一致。 -
FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。如果和singleTask模式一起出现,若被启动的Activity已经存在栈中,则清除其之上的Activity,并调用该Activity的onNewIntent方法。如果被启动的Activity采用standard模式,那么该Activity连同之上的所有Activity出栈,然后创建新的Activity实例并压入栈中。如果和FLAG_ACTIVITY_NEW_TASK 一起使用时,则是一种在另一个Task中定位现有Activity并将其放置在能够响应该意图的位置的方法。 -
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
如果一个Intent中包含此属性,则它转向的那个Activity以及在那个Activity其上的所有Activity都会在task重置时被清除出task
最后说明下activity启动时如何选择task:
-
先判断target activity能否在新task中启动
singleTask/singleInstance的activity本身具有在新task中启动的能力,standard/singleTop的activity要想拥有在新task中启动的能力,需要在设置Intent.FLAG_ACTIVITY_NEW_TASK -
判断target activity所在task
找一个taskAffinity的task去启动,找不到就新建一个(这里会忽略了singleInstance独占的task) -
根据TargetActivity的启动模式判断会如何启动
五、相关问题
onWindowFocusChanged
在Activity窗口获得或失去焦点时被调用,
1、创建时首次呈现在用户面前;
2、当前Activity被其他Activity覆盖;
3、当前Activity转到其他Activity或按Home键回到主屏,自身退居后台;
4、用户退出当前Activity。
以上几种情况都会调用onWindowFocusChanged,并且当Activity被创建时是在onResume之后被调用,当Activity被覆盖或者退居后台或者当前Activity退出时,它是在onPause之后被调用
这个方法在某种场合下还是很有用的,例如程序启动时想要获取视特定视图组件的尺寸大小,在onCreate中可能无法取到,因为窗口Window对象还没创建完成,这个时候我们就需要在onWindowFocusChanged里获取
其他应用问题:
-
activity间传递数据
1)通过 Intent 方式传递参数跳转
2)通过广播方式
3)通过接口回调方式
4)借助类的静态变量或全局变量
5)借助 SharedPreference 或是外部存储,如数据库或本地文件 -
知晓当前activity
-
关闭所有activity
-
双击退出app
-
保存activity状态
-
转场动画实现方式:
- overridePendingTransition(int enterAnim,int exitAnim)
需要注意,在 startActivity 和 finish 之后调用。
- 设置 Application style
- 使用ActivityOptions
面试常见知识点
Activity A跳转到Activity B 两者经历怎样的生命周期(考虑B不同的启动模式)
未配置configchanges情况下屏幕发生旋转时 当前Activity的生命周期是怎样变化的
关于Activity启动模式深入分析 参考