Android开发艺术探究(一):Activity的生命周期和启动模式
1. Activity生命周期的全面解析
1.1. 典型情况的生命周期
onCreate->onStart->onResume->onPause->onStop->onDestroy
1.1.1. Activity第一次启动时:onCreate--onStart--onResume
1.1.2. 打开的Activity切换到桌面或者打开新的Activity时:当前Activity执行onPause--onStop,特殊情况,当打开一个透明主题的Activity时,只执行onPause
1.1.3. 当用户再次回到原Activity时:onRestart--onStart--onResume
1.1.4. 当用户按back键返回时:onPause--onStop--onDestroy
1.1.5. 当Activity被系统回收时再次打开:onCreate--onStart--onResume
1.1.6. 从AActivity中打开BActivity时:先执行AActivity的onPause,然后执行BActivity的onCreate、onStart、onResume,最后执行AActivity的onStop
注:我们尽量在onStop中做操作,而不要在onPause中做,使新的Activity能够尽快显示
1.2. 异常情况下的生命周期分析
1.2.1. 情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建:比如横竖屏切换时
此种情况下,Activity会调用onSaveInstanceState方法保存数据,调用时机为onStop之前,它和onPause没有先后关系,重新创建时会调用onRestoreInstanceState方法恢复数据,调用时机为onStart方法之后。我们发现在onCreate方法中也有saveInstanceState参数,我们也可以使用它做恢复数据操作,它与onRestoreInstanceState的区别在于:使用onCreate恢复数据时,需要对saveInstanceState进行判null操作,因为在创建新的Activity时,它是null的,而使用onRestoreInstanceState方法则不需要。
1.2.1.1. 竖屏切横屏
执行onPause、onSaveInstanceState--onStop--onDestroy--onCreate--onStart--onRestoreInstanceState、onResume
1.2.1.2. 横屏切竖屏
执行onPause、onSaveInstanceState--onStop--onDestroy--onCreate--onStart--onRestoreInstanceState、onResume
--onPause、onSaveInstanceState--onStop--onDestroy--onCreate--onStart--onRestoreInstanceState、onResume
1.2.1.3. 修改AndroidManifest.xml,把该Activity添加 Android:configChanges="orientation",竖屏切横屏
执行onPause、onSaveInstanceState--onStop--onDestroy--onCreate--onStart--onRestoreInstanceState、onResume
1.2.1.4. 修改AndroidManifest.xml,把该Activity添加 Android:configChanges="orientation",横屏切竖屏
执行onPause、onSaveInstanceState--onStop--onDestroy--onCreate--onStart--onRestoreInstanceState、onResume
--onConfigurationChanged
1.2.1.5. 修改AndroidManifest.xml,把该Activity添加 Android:configChanges="orientation|keyboardHidden",竖屏切横屏
执行onConfigurationChanged
1.2.1.6. 修改AndroidManifest.xml,把该Activity添加 Android:configChanges="orientation|keyboardHidden",横屏切竖屏
执行onConfigurationChanged、onConfigurationChanged
注:自从Android 3.2(API 13),在设置Activity的android:configChanges="orientation|keyboardHidden"后,还是一样会重新调用各个生命周期的。因为screen
size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和TargetSdkVersion属性大于等于13的情况下,如果你想阻止程序在运行时重新加载Activity,除了设置"orientation",你还必须设置"ScreenSize"。
解决方法:
AndroidManifest.xml中设置android:configChanges="orientation|screenSize“
1.2.2. onConfigurationChanged的使用
首先:权限声明
<uses-permission Android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission>
其次:声明Activity要捕获的事件类型
如:Android:configChanges="orientation|keyboard"
最后:重写Activity的onConfigurationChanged方法,如:
1.2.2.1. configChanges可选属性
“mcc“ |
The IMSI mobile country code (MCC) has changed — that is, a SIM hasbeen detected and updated the MCC.移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。 |
“mnc“ |
The IMSI mobile network code (MNC) has changed — that is, a SIM hasbeen detected and updated the MNC.移动网号,在一个国家或者地区中,用于区分手机用户的服务商。 |
“locale“ |
The locale has changed — for example, the user has selected a new language that text should be displayed in.用户所在地区发生变化。 |
“touchscreen“ |
The touchscreen has changed. (This should never normally happen.) |
“keyboard“ |
The keyboard type has changed — for example, the user has plugged in an external keyboard.键盘模式发生变化,例如:用户接入外部键盘输入。 |
“keyboardHidden“ |
The keyboard accessibility has changed — for example, the user has slid the keyboard out to expose it.用户打开手机硬件键盘 |
“navigation“ |
The navigation type has changed. (This should never normally happen.) |
“orientation“ |
The screen orientation has changed — that is, the user has rotated the device.设备旋转,横向显示和竖向显示模式切换。 |
“fontScale“ |
The font scaling factor has changed — that is, the user has selected a new global font size.全局字体大小缩放发生改变 |
1.2.3. onNewIntent的使用
该Activity已经存在,如LaunchMode为SingleTop或者singleTask时,再次打开该Activity,通过执行onNewIntent传递数据。
生命周期为:onPause->onStop->onNewIntent->onRestart->onStart->onResume
1.2.4. 情况2:资源不足导致优先级低的Activity被杀死
该情况下也会执行onSaveInstanceState与onRestoreInstanceStae方法保存与恢复数据。
1.2.4.1. 前台Activity优先级最高
1.2.4.2. 可见但非前台的Activity优先级次之
1.2.4.3. 后台Activity优先级最低
2. Activity的启动模式
2.1. Activity的LaunchMode
2.1.1. Standard
标准模式:每次启动一个Activity就会创建一个新的实例。该模式下,一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。谁启动这个Activity,这个Activity就会属于那个Activity属于的那个任务栈。
2.1.2. singleTop
栈顶复用模式:如果新的Activity已经位于任务栈的栈顶,就不会重新被创建,而是执行onNewIntent方法接受参数。如果新的Activity不在栈顶,则会被再次创建。
2.1.3. singleTask
栈内复用模式:这是一种单例模式,如果新的Activity已经存在于该栈内,则不会被重新创建,而是执行onNewIntent方法接收参数。此时该Activity会被置于栈顶,它之前的Activity会被踢出该任务栈。
2.1.4. singleInstance
单实例模式:这是一种加强版的singleTask模式。该模式下的Activity单独占用一个任务栈,系统在创建该
Activity时,会为它创建一个新的任务栈。后续的请求均不会创建新的Activity,除非这个任务栈被系统销毁。
执行onNewIntent方法接收参数。
2.2. Activity的Flags
Activity的Flags有很多,这里只分析一些常用的标记位。
2.2.1. Intent.FLAG_ACTIVITY_NEW_TASK
指定Activity的启动模式为“singleTask”
2.2.2. Intent.FLAG_ACTIVITY_SINGLE_TOP
指定Activity的启动模式为“singleTop”
2.2.3. Intent.FLAG_ACTIVITY_CLEAR_TOP
具有此标识位的Activity创建时,和它在同一个任务栈中的并且在它之上的Activity都要出栈。它一般与Intent.FLAG_ACTIVITY_NEW_TASK一起使用。
2.2.4. Intent.FLAG_ACTIVITY_NO_HISTORY
具有该标识位的Activity,启动新的Activity后就会被销毁,不会保留在任务栈中。
2.2.5. Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
使用该标记符的Activity不会出现在Activity列表中,也即我们从最近应用里面查看不到我们启动的这个activity。等同于在xml指定Activity的属性:android:excludeFromRecents=”true”
2.3. 启动模式的使用方式
2.3.1. 通过配置AndroidManifest.xml为Activity指定启动模式
2.3.2. 通过Intent中设置标识位(addFlags方法)为Activity指定启动模式
3. IntentFilter的匹配规则
Intentfilter:意图过滤器。它包括action、category、data。
启动Activity包括两种方式,显式调用和隐式调用。
显式调用:明确指定被启动对象的组件信息,包括包名与类名,最为常用。
隐式调用:通过匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配则无法启动目标Activity。
下面为过滤规则的示例:
一个匹配列表中的action、category、data可以有多个,只有一个Intent同时匹配action类别、category类别、data类别时才算完全匹配,只有完全匹配才能启动该Activity。另外,一个Activity可以有多个匹配列表(intent-filter),只要匹配一个就可以成功启动该Activity。
3.1. Action
3.1.1. Action的匹配规则
Action是一个字符串。一个过滤规则可以有多个action,Intent中的Action能够和过滤规则中的任一个action相同即可匹配成功。如果Intent中没有action则匹配失败。另外,action区分大小写,大小写不同字符串相同也会匹配失败。
3.1.2. 系统预定义的Action
"android.intent.action.MAIN"------------------动作:作为主入口点启动,不需要数据。
"android.intent.action.CALL"-------------------动作:拨打电话,被呼叫的联系人在数据中指定。
详细查看:http://blog.****.net/mc_hust/article/details/46300929
3.2. Category
3.2.1. Category的匹配规则
Category是一个字符串。Category的匹配规则与action不同,Intent中可以没有category,但是如果有,不管有几个,每个Category都必须与过滤规则中的Category相对应。
为什么Intent中可以没有Category呢?因为系统在调用startActivity和startActivityForResult时,默认加了“android.intent.category.DEFAULT”,这样就匹配了前面示例的第三个Category。同时,为了Activity能被隐式调用,必须在intent-filter中加入“android.intent.category.DEFAULT”。
3.2.2. 系统预定义的Category
“android.intent.category.DEFAULT”----------------------------------默认的category
“android.intent.category.LAUNCHER”--------------------------------决定应用程序是否显示在程序列表里
...
3.3. Data
3.3.1. Data的匹配规则
Data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中也必须定义可匹配的Data。
3.3.2. Data的定义语法
Data由两部分组成:mimeType与 URI :
mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和 video/*等,可以表示图片、音频和视频等。
URI包含的东西比较多,结构为:
<scheme>://<host>:<post>/[<path>|<pathPrefix>|<pathPattern>]
Scheme:URI 的模式,如http、file、content等,如果不指定scheme,则整个uri无效
Host:uri的主机名,如www.baidu.com,如果不指定host,则整个uri无效
Port:uri的端口号,如8080,只有指定了scheme与host,port才有意义。
Path、pathPrefix、pathPattern:这三个参数为路径信息。其中path为完整路径,pathPattern也是完整路径,不过它可以使用通配符’*’,’*’表示0个或任意个。pathPrefix为路径前缀。
3.4. Intent指定Action、Category、data的方式