Activity是整个Android app直接与用户交互的核心组件。作为四大组件中出现频率最高的组件,我们在Android的各个地方都能看到它的影子。Activity中文直接翻译为"活动",其实翻译成"界面"更好理解。正常情况下,除了Window、Dialog和Toast,我们能看到的界面的确只有Activity。
深入了解Activity的工作模式、生命周期与管理模式,是了解Android系统的基础,也是开发过程中必备的基本技能。
谷歌给了我们一张图来表示Activity的生命周期,他希望Activity能被开发者所控制,而不是一匹脱缰的野马。下图是最经典的,也是我们最常见的:
(1)onCreate():create表示创建,这是Activity生命周期的第一个方法,也是我们在android开发中接触的最多的生命周期方法.它本身的作用是进行Activity的一些初始化工作,比如使用setContentView加载布局,对一些控件和变量进行初始化等.但也有很多人将很多与初始化无关的代码放在这,其实这是不规范的.此时Activity还在后台。不可见,所以动画不应该在这里初始化,因为看不到……
(2)onStart():start表示启动,这是Activity生命周期的第二个方法.此时Activity已经可见了,但是还没出现在前台,我们还看不到,无法与Activity交互。其实将Activity的初始化工作放在这也没有什么问题,放在onCreate中是由于官方推荐的以及我们开发的习惯。
(3)onResume():resume表示继续、重新开始,这名字和它的职责也相同。此时Activity经过前两个阶段的初始化已经蓄势待发。Activity在这个阶段已经出现在前台并且可见了。这个阶段可以打开独占设备。
(4)onPause():pause表示暂停,当Activity要跳到另一个Activity或应用正常退出时都会执行这个方法。此时Activity在前台并可见,我们可以进行一些轻量级的存储数据和去初始化的工作,不能太耗时,因为在跳转Activity时只有当一个Activity执行完了onPause方法后另一个Activity才会启动,而且android中指定如果onPause在500ms即0.5秒内没有执行完毕的话就会强制关闭Activity。从生命周期图中发现可以在这快速重启,但这种情况其实很罕见,比如用户切到下一个Activity的途中按back键快速得切回来。
(5)onStop():stop表示停止,此时Activity已经不可见了,但是Activity对象还在内存中,没有被销毁。这个阶段的主要工作也是做一些资源的回收工作。
(6)onDestroy():destroy表示毁灭,这个阶段Activity被销毁,不可见,我们可以将还没释放的资源释放,以及进行一些回收工作。
(7)onRestart():restart表示重新开始,Activity在这时可见,当用户按Home键切换到桌面后又切回来或者从后一个Activity切回前一个Activity就会触发这个方法。这里一般不做什么操作。
(1)onCreate()和onStart()之间有什么区别?
①可见与不可见的区别。前者不可见,后者可见。
②执行次数的区别。onCreate方法只在Activity创建时执行一次,而onStart方法在Activity的切换以及按Home键返回桌面再切回应用的过程中被多次调用。因此Bundle数据的恢复在onStart中进行比onCreate中执行更合适。
③onCreate能做的事onStart其实都能做,但是onstart能做的事onCreate却未必适合做。如前文所说的,setContentView和资源初始化在两者都能做,然而想动画的初始化在onStart中做比较好。
(2)onStart()方法和onResume()方法有什么区别?
①是否在前台。onStart方法中Activity可见但不在前台,不可交互,而在onResume中在前台。
②职责不同,onStart方法中主要还是进行初始化工作,而onResume方法,根据官方的建议,可以做开启动画和独占设备的操作。
(3)onPause()方法和onStop()方法有什么区别?
①是否可见。onPause时Activity可见,onStop时Activity不可见,但Activity对象还在内存中。
②在系统内存不足的时候可能不会执行onStop方法,因此程序状态的保存、独占设备和动画的关闭、以及一些数据的保存最好在onPause中进行,但要注意不能太耗时。
(4)onStop()方法和onDestroy()方法有什么区别?
onStop阶段Activity还没有被销毁,对象还在内存中,此时可以通过切换Activity再次回到该Activity,而onDestroy阶段Acivity被销毁。
开发者当然不必实现所有的生命周期方法,但知道每一个生命周期状态的含义,可以让我们更好地掌握Activity,让它能更好地完成你所期望的效果。
上面的生命周期图列举了Activity的生命周期状态,但其中只有三个状态是稳定的,而其他状态都只是过渡状态,很快就会消失。
Resumed
这个状态,也就是前面所说的Activity/Running形态,此时候,Activity处于Activity栈的最顶层,处理用户的交互。
Paused
Activity失去焦点,被一个新的非全屏的Activity或者一个透明的Activity放置在栈顶时,Activity就转换成了paused形态,他失去了与用户交互的能力,所有状态信息、成员变量都还保留着,只有在系统内存极低的情况下,才会被系统回收。
Stopped
如果一个Activity被另一个Activity完全覆盖,那么Activity就会进入stop形态,此时他不在可见,但依然保留着所有的状态和成员变量。
在系统调用onCreate()方法之后,就会马上调用onStart(),然后继续调用onResume()来进图运行状态,最后都会停在onResume()状态,完成启动,系统会调用onDestroy()来结束一个Activity的生命周期让他毁掉kill状态。
以上就是一个Activity的启动和销毁的过程。
当栈顶的Activity部分不可见的时候,就会导致Activity进入onPause()方法,当结束阻塞后,就会调用onResume()方法来回复到Resume形态。
栈顶的Activity部分不可见的时候,实际上后续会有两种可能,从部分不可见到可见,也就是恢复过程,从部分不可见到完全不可见,也就是停止过程,系统在当前Activity不可见的时候调用onPause()方法。
这部分是最复杂的,我将在第四部分进行说明。
对于这个问题的讨论,我们通过输出日志的方式进行(如果是真机的话,记得将手机设置成自动旋转,模拟器的话点击工具栏的旋转按钮)。
从日志可以清晰看出:设备旋转后,系统会销毁当前Activity实例,然后创建一个新的Activity实例.再次旋转设备,又一次见证这个销毁与再创建的过程。
因此,我们在开发过程中,如没有特殊需求,为了避免用户因旋转设备导致Activity生命周期改变,而出现意想不到的问题,我们会进行设置设置.方法如下:
方法一:在AndroidManifest.xml中设置
android:screenOrientation="portrait"(始终保持竖屏的生命周期)
android:screenOrientation="landscape"(始终保持横屏的生命周期)
***********************************
方法二:在代码中设置
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); (始终保持竖屏的生命周期)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);(始终保持横屏的生命周期)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_lifecy);
}
在Activity中,onSaveInstanceState()是开发人员进阶必知的方法,其中一个参数的onSaveInstanceState(Bundle outState)来自AppCompatActivity,两个参数的onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)来自Activity.前者是我们使用最多的。本文的讨论,若无特殊说明,均指一个参数这个方法。
该方法通常在onStop()方法之前由系统调用,除非用户按后退键。(记住,按后退键就是告诉Android,Activity用完了.随后,该Activity就完全从内存中被抹掉,自然,也就没有必要为重建保存数据了。)
方法onSaveInstanceState(Bundle)的默认实现要求所有Activity视图将自身状态数据保存在Bundle对象中。Bundle是存储字符串键与限定类型值之间映射关系(键-值对)的一种结构。
可通过重写onSaveInstanceState(Bundle)方法,将一些数据保存在bundle中,然后在onCreate(Bundle)方法中取回这些数据。代码片段如下:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_INDEX,1); //存值
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecy);
if (savedInstanceState!=null){
int anInt = savedInstanceState.getInt(KEY_INDEX,0);
}
}
注意:在Bundle中储存和恢复的数据类型只有是基本数据类型,以及可以实现Serializable或Parcelable接口的对象。在Bundle中保存定制类对象不是个好办法,因为你取回的对象可能已经没用了。比较好的做法是,通过其他方式保存定制类对象,而在Bundle中保存标识对象的基本类型数据。
从刚刚的代码片段大家可以发现,其用法其实和sharepreference差不多,的确是这样。
用户离开当前Activity界面,或Android需要回收内存时,Activity也会被销毁(例如:用户按了Home键,然后去玩王者荣耀)。基于用户体验考虑,Android从不会为了回收内存而去销毁可见的Activity。只有在调用过onStop()并执行完成后,Activity才会被标为可销毁。
的确,系统随时会销毁已停止的Activity,但我们其实不用担心数据丢失。因为Activity停止时,会调用onSaveInstanceState(Bundle)方法的。那么,保存在onSaveInstanceState(Bundle)的数据该如何幸免于难呢?调用该方法时,用户数据随即被保存在Bundle对象中,然后操作系统将Bundle对象放入Activity记录中。为了便于大家理解,我们增加一个暂存状态到Activity生命周期中,如下图:
Activity暂存后,其对象不再存在,但操作系统会将Activity记录对象保存起来。这样,在需要恢复Activity时,操作系统可以使用暂存的Activity记录重新激活Activity。
注意:Activity进入暂存状态并不一定需要调用onDestory()方法。不过,onStop()和onSaveInstanceState(Bundle)方法是两个可靠的方法(除非设备出现重大故障)。因而,常见的做法就是,重写onSaveInstanceState(Bundle)方法,在Bundle对象中,保存当前Activity的小的或者暂存状态的数据;重写onStop方法,保存永久性数据,如用户编辑的文字等。onStop()方法调用完,Activity随时会被系统销毁,所以用它保存永久性数据。
那么,暂存的Activity记录到底能够保存多久呢?前面说了,用户按了后退键后,系统会彻底销毁当前的Activity。此时,暂存的Activity记录同时被清除。此外,系统重启的话,暂存的Activity记录也会被清除。
点我下载