Activity生命周期
典型情况下的生命周期分析
Activity生命周期方法
onCreate: 生命周期第一个方法,在这做初始化工作。
onRestart: 表示Activity正在重新启动。当Activity从不可见重新变为可见状态时被调用。比如按Home键切换桌面,或者打开了另外一个新的Activity。
onStart: 表示Activity正在启动,这种状态时,Activity可见但是没有出现在前台(无法与用户交互,用户还看不到)。
onResume: 表示Activity已经可见(可以和用户进行交互)。和onStart对比,两个方法都表示Activity可见,但是onStart的时候Activity还在后台,onResume的时候,Activity才显示到前台。
onPause: 表示Activity正在停止。此时可以做一些存储数据,停止动画等不耗时操作。如果做了耗时操作,会影响新Activity的显示。
onStop: 表示Activity即将停止,在onPause后调用。可以做一些稍微重量级的回收工作,同样不能太耗时。
onDestory:表示Activity即将被销毁,最后一个生命周期方法。可以做一些资源释放或者回收工作。
Activity生命周期图示
如果当前Activity采用了透明主题,那么当前Activity不会回调onStop。
当用户再次返回原Activity,回调如下:onRestart->onStart->onResume。
onStart和onStop是从Activity是否可见角度来回调的。onResume和onPause是从Activity是否在前台(用户可交互)这个角度来回调的。除此之外没其他明显区别。
如果当前Activity为A,现在用户新打开了一个Activity-B,那么B的onResume是在A的onPause之后执行的。所以在onPause中不要做耗时操作,否则会影响新Activity的显示。
异常情况下的生命周期分析
情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
比如在Drawable中设置了横竖屏不同的图片。如果此时突然旋转屏幕,由于系统配置发生了改变,在默认情况下Activity就会销毁并且重新创建(可以在AndroidManifest.xml文件中配置不重建Activity),此时除开默认的生命周期外,系统会调用 onSaveInstanceState来保存Activity当前状态,调用实际在onStoop之前,但是和onPause没有前后关系,可能在onPause前,也可能在后。在Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁前onSaveInstanceState保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。因此我们可以通过onRestoreInstanceState 和 onCreate方法(savedInstanceState参数不为空)判断当前Activity是否是重建的。如果是重建的我们就可以取出销毁前保存的数据并回复,onRestoreInstanceState调用时机在onStart之后。
在onSaveInstanceState和onRestoreInstanceState中,系统会自动做一些保存回复工作,比如当前Activity的视图结构。每一个View也都有这两个方法,像ListView滚动位置,TextView的文本等,这些状态都会自动保存回复。所以自定义 View 中需要有重建后回复数据的需求的话,需要实现这两个方法。
情况2:资源内存不足导致低优先级的 Activity被杀死
Activity的优先级
前台Activity--正在和用户交互的Activity,优先级最高。
可见但非前台Activity--比如在Activity弹出一个对话框,此时Activity不能操作,但是可见。
后台Activity--已经被暂停的Activity,比如执行了onStop。
说明
当系统内存不足,系统就会按照上面优先级杀死Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储/恢复数据。如果一个进程中没有四大组件在执行,那么这个进制很快会被系统杀死,因此一些后台工作不适合脱离四大组件而独立允许在后台中。比较好的后台方法:在后台放入Service中,保证进程有一点优先级。
如果不想再系统配置发生改变时,重建Activity。可以给Activity指定configChanges属性。比如 android:configChanges="orientation", 就表示屏幕旋转的时候,不重建Activity。常用configChanges值:orientation-屏幕方向发生改变,uiMod-屏幕状态发生改变,比如开启夜间模式,locale-一般为切换了系统语言,keyboardHidden-键盘可访问新发生了改变,比如弹出了键盘。
Activity启动模式
LainchMode
Standard 标准模式
标准模式,是系统的默认模式。
每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。
谁启动了这个Activity,那么这个Activity就会允许在启动他的那个Activity所在的栈中。比如A启动了B(标准模式),B就会进入A所在的栈中。
常见报错(Calling startActivity from XXX, Is this really what you want)。原因:使用非Activity类型的Context(如ApplicationContext)启动了标准模式的Activity就会报这个错。因为ApplicationContext没有任务栈,所以启动的 Activity不知道去哪儿。解决办法:将待启动的Activity指定 FLAG_ACTIVITY_NEW_TASK 标志位,这样启动的时候就会创建一个新的任务栈。
singleTop 栈顶复用模式
如果新Activity位于任务栈的栈顶,那么此Activity就不会被创建。同时他的onNewIntent方法被回调,但是onCreate,onStart不会被系统调用,因为他并没有重新发生改变。
如果新Activity已存在但是不是在栈顶,那么新Activity任然会创建。
singleTask 栈内复用模式
单实例模式,只要Activity在一个栈中存在,那么启动此Activity就不会创建新实例。和singleTop一样,同样会回调onNewIntent方法。
singleTask 有cleanTop的效果。如果D所需任务栈为S1,S1有ADBC。C位于栈顶。那么此时D不会重新创建,系统会把D切换到栈顶并调用其OnNewIntent方法。同时栈内所有D上面的 Activity全部出栈。于是最终S1的情况是AD.
singleInstance 单实例模式
加强singleTask,具有singleTask所有特性。
除上面外,有此种模式的Activity只能单独位于一个任务栈。
Activity的Flags
常用的标识位
FLAG_ACTIVITY_NEW_TASK : 指定Activity启动模式为"sinleTask"
FLAG_ACTIVITY_SINGLE_TOP: 指定Activity启动模式为"singeTop"
FLAG_ACTIVITY_CLEAR_TOP: 具有此标志位的Activity启动时,他上面的Activity都要出栈。sinleTask默认具有这个特性。如果被启动的Activity启动模式为"standard 标准模式"同时具有这个Flag的话,那么它连同它之上的Activity都要出栈,同时系统会创建新的Activity实例并放入栈顶。
FLAG_ACTIVITY_EXCLUED_FROM_RECENTS: 具有这个标记的Activity不会出现在历史Activity列表中。也可以在XML指定Activity的属性android:excludeFromRecents="true"
IntentFilter的匹配规则
如果显式调用和隐式调用都存在的话,以显示调用为准。
只有一个Intent同时陪陪action,category,data才能启动目标Activity。一个Intent可以有很多组intent-filter,只要匹配上一组就可以启动对应的Activity。
action
action为字符串,系统预定义了一些,同时我们也可以自定义。
匹配规则:intent中的action必须存在,且必须和过滤规则中的任意一个action相同,就算匹配成功,区分大小写。
category
category为字符串,系统预定义了一些,同时我们也可以自定义。
intent中不管有几个category,对于每个category来说,它必须示过滤规则中已经定义了的。intent中可以没有category。
如果intent没有设置category,那么系统会默认加上 “android.intent.category.DEFAULT” 这个category。
为了Activity能接受隐式调用,必须在intent-filter中指定“android.intent.category.DEFAULT” 这个category,原因示上条。
data
如果过滤规则中定义了data,那么intent中必须也要定义可匹配的data。
data由两部分组成,mimeType(媒体类型,比如image/jpeg等)和 URI。
URI的结构: scheme://host:port/path
举例:content://com.joker.project:200/folder/etc
scheme表示 URI的模式
隐式调用需要注意的地方
当通过隐式方式启动Activity的时候,需要判断一下是否由Activity能匹配。
判断方法有两种,采用PackageManager的resolveActivit的方法或者Intent的resolveActivity方法。