Activity知识点总结
更新2020年10月13日11:55:42:3.5中增加了用代码设置启动方式的内容
前言
参考项目:https://github.com/wodongx123/ActivityDemo/tree/master
仅供参考,都是正文的代码。
1. Activity的定义
Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。
2.Activity的生命周期
2.1任务和返回栈
- Android实际上使用任务(task)来管理Activity,每一个任务就是一个返回栈(back stack)。任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。
- 每当启动一个新的Activity的时候,这个新activty就会入栈。
- 一般情况下(也就是无任何其他因素影响的情况下),在点击手机的返回键的时候栈顶的Activity就会出栈,同时这个Activity也会被销毁。
2.2运行状态
运行状态(Running):当一个Activity处于栈顶的时候,就是处于运行状态了,对于Android系统而言,它不会愿意强制销毁这样的进程。
暂停状态(Paused):当Activity不处于栈顶,但是仍然可见的时候,就是处于暂停状态了。对于Android系统而言,哪天内存非常吃紧的时候,它会强制回收处于暂停状态的Activity。
这样子想理解可能特别痛苦,简单的举例:处于栈顶的Activity(也就是运行状态的activty)忽然间弹出了一个对话框(实际上并不是对话框,而是一个并不占满全屏的Activity),大家都清楚,对话框一般不会铺满整个屏幕,此时Activity仍然有部分可见,这个时候这个Activity就进入暂停状态了。
停止状态(Stopped):当Activity不处于栈顶,且完全不可见的时候,就是处于停止状态了。停止状态下的Activity不会被回收,其中的变量和状态都会保存。对于Android系统而言,哪天内存有些吃紧的时候,它会强制回收处于停止状态的Activity。
销毁状态(Killed):当Activity从返回栈中移除之后,就是处于销毁状态了。对于Android系统而言,它正在回收分配给这个Activity的内存。
2.3生命周期
2.3.1生命周期方法
onCreate():Activity被创建的时候调用,初始化Activity所需要的控件、布局、资源和事件等。
onStart():此时Activity对于用户可见,但是不能交互。
onResume():当Activity准备好和用户交互的时候调用,此时Activity一定处于返回栈的栈顶。
onPause():Activity因为各式各样的原因(比如切回到桌面,比如打开新的Activity)即将变得不可见的时候,会调用这个方法。
onStop():Activity完全不可见的时候调用,此时Activity对于用户就是完全不可见了。
onRestart():当Activity要从不可见状态重新变成可见状态时,会在onStart()之前调用这个方法,也就是重新启动活动。
onDestroy():Activity要完全被销毁的时候调用,之后Activity的实例将会消失。
结合刚才的运行状态就可以得知:
在onResume()之后Activity处于运行状态
在onPause()之后Activity处于暂停状态
在onStop()之后Activity处于停止状态
在onDestroy()的时候activtiy处于销毁状态
2.3.2生命周期情况分析
单独Activity分析
- acitivity启动:onCreate() --> onStart() --> onResume() -->Activity正在运行
- Activity暂停:运行状态 --> onPause()
- Activity停止:运行状态 --> onPause() --> onStop() --> Activity完全不可见
- Activity重启:停止状态 --> onRestart() -->onStart() --> onResume() -->Activity正在运行
- Activity销毁:运行状态–> onPause() --> onStop() --> onDestroy() --> 系统回收内存
多个Activity分析
- 从Activity_A中启动Activity_B
activtiy_A:3
Activity_B:1
具体顺序为:A onPause -> B onCreate -> B onStart -> B onResume -> A onStop - 从Activity_B点击返回键回到Activity_A
Activity_A:4
Activity_B:5
onSaveInstanceState和onRestoreInstanceState
- 在生命周期方法中,只有onCreate()是带有参数的。这个参数就是Activity所设置的保护机制。每当Activity遇到了意外情况1被强制关闭的时候,Activity会在关闭的前一刻调用内部函数onSaveInstanceState(),我们可以通过重写这个方法,让Activity被回收的时候用bundle保存一些重要数据,bundle是一种类似字典一样的数据类型,通过key-value模式来保存数据。
- 顺带一提,onSaveInstanceState()的执行时间(如果有执行)是在onStop()之后,onDestroy()之前。
- 而当调用了onSaveInstanceState()之后,再次启动Activity的时候,就会调用onRestoreInstanceState(),可以发现这个参数和onCreate()参数名相同,而事实上也是如此,两者是同一个变量,也就是说无论是通过onCreate()还是onRestoreInstanceState()均能恢复数据。但是要注意的是,如果onSaveInstanceState()没有执行,那么onCreate()中的参数就会为空,因此在使用onCreate()恢复数据的时候要判断是否为空。
- 顺带一提,onRestoreInstanceState()的执行时间(如果有执行)是在onStart()之后,onResume()之前。
//每当Activity遇到意外情况的时候,重写这个方法来保存数据
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState: ");
outState.putString("key0", "value0");
outState.putInt("key1", 1);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, "onRestoreInstanceState: ");
}
Activity的横竖屏切换
对于不做任何特殊改动的Activity而言,横竖屏切换实际上会先销毁当前的Activity,然后再重新创建一个新的采用横屏布局的Activity。
所以生命周期为:Activity销毁 --> Activity创建
I/MainActivity: onPause:
I/MainActivity: onStop:
I/MainActivity: onSaveInstanceState:
I/MainActivity: onDestroy:
I/MainActivity: onCreate:
I/MainActivity: onStart:
I/MainActivity: onRestoreInstanceState:
I/MainActivity: onResume:
Activity的屏幕大小切换
可能你会很好奇,手机屏幕一共就那么大,都是定死的东西,怎么可能切换屏幕大小呢。事实上,以前是不可以,但是现在的手机都有一个叫做分屏的功能,在分屏的情况下,屏幕大小只有原先的一半,这个时候屏幕大小就变动了;而分屏恢复成全屏又回再变动一次。
在切换屏幕大小的时候,实际上也是先销毁当前Activity,再创建一个新的Activity。生命周期也是先走销毁,然后再重新创建。
只执行onPause()不执行onStop()的特殊情况2
想要造成这种情况,我们需要在原本的Activity上启动一个不是全屏的Activity,新建一个DialogActiviy,然后在androidmanifest.xml中改变DialogActivity的类型为窗口类型。
<Activity
android:name=".DialogActivity"
android:theme="@android:style/Theme.Dialog" >
</Activity>
在原来的Activity中设置一个按钮,点击后打开dialogActivity,这个时候原先的Activity就只执行onPause()而不会执行onStop()。
生命周期如下:
MainActivity:onPause()
DialogActivity:onCreate() --> onStart() --> onResume()
顺便一提,在保持着打开两个Activity的状态下,意外切出(比如息屏,比如切回到桌面)时的生命周期如下:
I/DialogActivity: onPause:
I/MainActivity: onStop:
I/MainActivity: onSaveInstanceState:
I/DialogActivity: onStop:
I/DialogActivity: onSaveInstanceState:
然后,在此情况下强制关闭app的生命周期如下:
I/MainActivity: onDestroy:
I/DialogActivity: onDestroy:
Activity弹出Dialog、Activity下拉菜单栏
Dialog的弹出实际上并不影响Activity的生命周期,也就是说,如果Activity中弹出了一个Dialog,那么Activity不会调用任何生命周期中的方法。
下拉菜单栏实际上也不会影响Activity的生命周期。
Activity销毁却没有执行onDestroy()
- 在任务栈中如果有多个Activity,然后从后台强制清除任务(就是从最近任务中强制关闭应用),这个时候,只会调用处于返回栈最下面的Activity的onDestroy(),其他的不会调用。
- 如果有多个返回栈(就是启动了带有SingleInstance的Acitivity),只会调用当前返回栈最下面的Activity的onDestroy()。
3.Activity的启动方式
Andoird系统通过不同的Activity启动方式,来实现在不同的情况下,Activity有不同的用法。
假设:有一个Activity_A和Activity_B,它们都有AB个按钮,点击A之后,会生成一个Activity_A;点击B之后,会生成一个Activity_B,初始状态是栈内只有一个Activity_A。
3.1标准模式(standard)
标准模式(standard):每当生成一个新的Activity的时候,其实本质上是生成一个Activity的实例,这也就是说,你可以嵌套重复打开同一个Activity无数次。
- Activity_A在点击A按钮后,会生成一个新的Activity_A,此时你的返回栈中就会有两个不同的Activity_A。
3.2栈顶复用模式(singleTop)
栈顶复用模式(singleTop):复用的意思就是把这个Activity拿来继续用,而不生成一个新的来使用。当Activity处于返回栈顶的时候,如果再次生成一个相同的Activity时,它不会创建新的Activity,而是继续使用当前的这个Activity。
当然如果这个Activity不在栈顶的时候,刚刚的一切就当无事发生,启动一个同样的Activity会生成一个新的实例。
- 假设Activity_A使用栈顶复用模式。在Activity_A中点击A按钮后,不会生成一个新的Activity_A,此时返回栈只有一个Activity_A。
- 假设Activity_A使用栈顶复用模式。在Activity_A中点击B按钮后,生成一个新的Activity_B,再点击Activity_B的A按钮,此时会生成一个新的Activity_A,返回栈内有两个Activity_A。
3.3栈内复用模式(singleTask)
栈内复用模式(singleTask):复用的意思同上,根据名字知道,当Activity处于返回栈内的时候,如果再次生成一个新的Activity时,不会创建新的而是使用现有的Activity,使用方法是:把压在这个activty上面的所有其他的Activity出栈(也就是把他们都销毁),使得这个Activity处于栈顶。
- 假设Activity_A使用栈内复用模式。在Activity_A中点击A按钮后,不会生成一个新的Activity_A,此时返回栈只有一个Activity_A。
- 假设Activity_A使用栈内复用模式。在Activity_A中点击B按钮后,生成一个新的Activity_B,再点击Activity_B的A按钮,不会生成一个新的Activity_A,Activity_B出栈,返回栈内只剩下一个Activity_A。
3.4单例模式(singleInstance)
单例模式(singleInstance):单例模式比较特殊,当创建一个单例模式的Activity的时候,程序会创建一个新的返回栈,且这个返回栈内有且只有它一个Activity。
- 假设Activity_B处于单例模式。Activity_A点击B后,创建一个新的返回栈B,里面只有Activity_B,再点击A后,原先的返回栈A创建一个新的Activity_A。
- 要注意的是,对于Android 的任务而言,没有特殊原因它不会特地的去切换返回栈,所以在点击返回键后,显示的不是Activity_B而是Activity_A。
3.5启动模式的设置方法
- 在AndroidManifest.xml文件中,修改Activity的启动模式属性launchMode即可。
<activity
android:name=".Activity_A"
android:launchMode="singleTop" >
</activity>
- 在startActivity时指定flag设置启动模式
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 指定为singleTask启动模式
//intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 指定为singleTop启动模式
//intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 启动时,同一个任务栈上面的所有Activity都会出栈,一般和singleTask一起出现
//intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); // 该Activity不会出现在历史Activity列表中
startActivity(intent);
4.其他
横竖屏切换的补充说明
一般情况下,想要开启横屏模式的Activity,首先创建一个layout-land文件夹。
- 将Android项目切换成Project模式。
- app -> src - > main -> res -> new directory,取名叫layout-land。
- 然后将你想要横屏的activity的对应xml重写,命名为同名(比如说原本是activity_main.xml,那么在layout-land目录下的文件也叫activity_main.xml)。
- 要注意,原本xml上的控件在横屏之后不能缺失,不然在横屏后会报错(报空指针错误,因为找不到控件了…)。
- 这样一来,Android系统会在你将屏幕横过来之后,自动的切换到横屏模式。
一般这样设置完,在切换成横屏的时候,会自动销毁Activity,然后重新创建一个新的横屏模式的activity,但是,也有办法可以在不销毁的同时直接切换到横屏。
在AndroidManifest.xml中这么改。
<application
………………
<!--android:configChanges可以在标出的情况发生时不重启Activity-->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
………………
</activity>
</application>
其中,orientation表示横竖屏切换,screenSize表示屏幕大小切换,两个一起加的原因是,横竖屏切换的同时,屏幕大小也会同时变化,所以要两个一起加才会在切换横竖屏的时候不重启Activity。
在设置之后,切换横竖屏不会调用任何生命周期方法。同时,使用分屏也不会调用任何生命周期方法了。
有关android:configChanges更多可以参考这篇:
https://blog.csdn.net/lkk790470143/article/details/79345971
执行onPause的补充说明
参考:Android 下拉通知栏时Activity的生命周期——重新理解onPause()
https://www.jianshu.com/p/781bc86f8042
结论:当且仅当Activity被另一个Activity所遮盖部分界面的时候,才会只执行onPause()不执行onStop()。
- 不触发的情况举例:
- 打开Dialog。
- 弹出Toast。
- 下拉状态栏。
统计Activity的各种时间
启动时间:在onCreate()和onResume()中记录时间,两者相减就是启动所花时间。
重启时间:在onRestart()和onResume()记录时间,相减。
工作时间:在onResume()和onPause()记录时间,相减。
选择后台的Activity销毁
有时候会出现这样的要求,用户现在在Activity_A,但是却要关闭处于后台的Activity_B,这种时候改怎么办呢。
- 对每个Activity都设置一个全局静态变量instance,然后在onCreate的时候将静态变量指向自己,这样在其他activity的话,也可以通过调用这个instance进行控制处于后台的Activity。但是这个方法不推荐使用,一是容易造成程序出错,二是不支持批量操作,如果我要同时关闭好几个Activity,那就很辛苦了。
- 封装一个ActivityController类,内部有一个List,每当有Activity启动的时候,就讲这个实例加入到List中,这样在要关闭的时候,只要从List中遍历就能找到要关闭的Activity了,而且也支持同时关闭所有Activity的功能(遍历所有的Activity关闭)。
- 一个采用弱引用的方法,本质上是方法1的解决内存泄漏后的改良版方法。
Android在别的Activity关闭某个指定的Activity - CSDN
https://blog.csdn.net/qq_36487432/article/details/82427753
参考材料
Android学习笔记(一) | Activity的生命周期 - 简书
https://www.jianshu.com/p/ec50675ed116
Android面试系列文章2018之Android部分Activity篇 - CSDN
https://blog.csdn.net/clandellen/article/details/79257489
第一行代码——Android(第2版)
p53 - p75
onSaveInstanceState的调用情况可以看这篇:
https://blog.csdn.net/wzy901213/article/details/79621944 ↩︎只执行onPause()不执行onStop()参考这篇:
https://blog.csdn.net/a872822645/article/details/62217965 ↩︎