《Android开发艺术探索》第1章读书笔记

来源:互联网 发布:qq三国hj和js 编辑:程序博客网 时间:2024/05/29 21:17

第1章 Activity的生命周期和启动模式


1.1  Activity的生命周期全面分析


1.1.1 典型情况下的生命周期分析


在正常情况下,Activity会经历如下生命周期:

(1)onCreate:Activity正在被创建,可以完成一些初始化工作。

(2)onRestart:Activity正在重新启动,当当前Activity从不可见重新变为可见状态时,就会被调用。

(3)onStart:Activity正在被启动,这时Activity已经可见,但是还没有出现在前台,可以理解为Activity已经显示出来,但是我们还看不到。

(4)onResume:Activity已经可见,并且出现在前台开始活动。

(5)onPause:Activity正在停止,正常情况下,紧接着onStop就会被调用,此时可以做一些存储数据、停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须先执行完,新Activity的onResume才会执行。

(6)onStop:Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。

(7)onDestroy:Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里,我们可以做一些回收工作和最终的资源释放。


Activity生命周期的切换过程:


几种情况的说明:

(1)如果新Activity采用了透明主题,那么当前Activity不会回调onStop。

(2)从整个生命周期来说,onCreate和onDestroy是配对的,分别标识着Activity的创建和销毁,并且只可能有一次调用。从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被多次调用。从Activity是否在前台来说,onResume和onPause是配对的,随着用户操作或者屏幕的点亮和熄灭,这两个方法可能被多次调用。

(3)onPause和onStop都不能执行耗时的操作,尤其是onPause,这也意味着,我们应当尽量在onStop中做操作,从而使得新Activity尽快显示出来并切换到前台。


1.1.2 异常情况下的生命周期分析


情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

(1)Activity在异常情况下终止,系统会调用onSaveInstanceState来保存当前Activity的状态,这个方法的调用时机是在onStop之前,但和onPause没有既定的时序关系。

(2)onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和OnCreate方法,onRestoreInstanceState的调用时机在OnStart之后;onRestoreInstanceState一旦被调用,其参数BundlesaveInstanceState一定是有值的,我们不用额外地判断是否为空,但是onCreate不行,onCreate如果是正常启动的话,其参数Bundle saveInstanceState为null,所以必须要额外判断;所以,异常情况下的数据恢复工作,建议放在onRestoreInstanceState中进行。

(3)在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一定的恢复工作,当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、listView滚动的位置等,这些View相关的状态系统都能够默认问我们恢复。【系统如果想保留/恢复TextView/EditText中用户输入的内容,此View必须要有id】

(4)系统只有在Activity异常终止的时候才会调用onSaveInstanceState和onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程,但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用。

 

情况2:资源内存不足导致低优先级的Activity被杀死

(1)如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台,这样进程很容易被杀死,比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

(2)系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建Activity,可以给Activity指定configChanges属性,如果没有在Activity的configChanges属性中指定相应选项的话,当相应配置发生改变后就会导致Activity重新创建。

(3)需要注意的是,当屏幕发生旋转时,屏幕尺寸会发生变化,当编译选项中的minSdkVersion和targetSdkVersion均低于13时,Activity不会重启,但当其中有一项低于13,就会导致Activity重启,所以当开发时有一项低于13,为了防止旋转屏幕时Activity重启,除了orientation,我们还要加上screenSize。

(4)如果给Activity指定过configChanges属性,相应系统配置发生变化时,Activity就不会重新创建,并且也不会调用onSaveInstanceState和onRestoreInstanceState来储存和恢复数据,取而代之的是系统调用了Activity的onConfigurationChanged方法,这个时候我们就可以做一些自己的特殊处理了。【反之,如果没有指定过configChanges属性,就不会调用onConfigurationChanged方法】


1.2 Activity的启动模式


1.2.1 Activity的LaunchMode

(1)standard:标准模式,系统的默认模式,每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。需要注意的是,当我们用ApplicationContext去启动standard模式的Activity的时候会报错,这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈,所以就有问题了。解决这个问题的方法就是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候待启动Activity实际上是以singleTask模式启动的。

(2)singleTop:栈顶复用模式,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。如果新Activity的实例已存在但不是位于栈顶,那么新Activity任然会重新创建。

(3)singleTask:栈内复用模式,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,系统会把已存在的Activity调到栈顶,同时系统会回调其onNewIntent,如果实例不存在,就创建新的实例并压入栈中。需要注意的是,由于singleTask默认具有clearTop的效果,新启动拥有此模式的Activity时,会导致栈中所有在此实例上面的Activity全部出栈。

(4)singleInstance:单实例模式,这是一种加强的singleTask模式,它除了具有singleTask模式的所以特性外,还加强了一点,那就是具有此模式的Activity只能单独地位于一个任务栈中。

 

几种情况的说明:

(1)任务栈是一种“先进后出”的栈结构,每按一次back键就会有一个Activity出栈,直到栈空为止,当栈中无任何Activity的时候,系统就会回收这个任务栈。

(2)什么是Activity所需要的任务栈呢?

(3)TaskAffinity,可以翻译为任务相关性,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。

(4)任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次再次调到前台。

(5)给Activity指定启动模式,有两种方法:第一种是通过AndroidMenifest为Activity指定启动模式,另一种是通过在Intent中设置标志位来为Activity指定启动模式;两者在应用时是有区别的,首先,优先级上,第二种方式的的优先级要高于第一种,当两种同时存在时,以第二种方式为准;其次,上述两种方式在限定范围上有所不同,比如,第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity指定singleInstance模式。

(6)虽然很绕,但真不错:


1.2.2 Activity的Flags

(1)标记位的作用有很多,有的标记位可以设定Activity的启动模式,比如,FLAG_ACTIVITY_NEW_TASK和F LAG_ACTIVITY_SINGLE_TOP等;还有的标记可以影响到Activity的运行状态,比如FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。

(2)这一节的内容直接翻译了Android官方文档(点击打开链接),但是经过实例验证,发现书中的描述不准确(或者说官方文档中的描述不准确),结论为:Flags并不能简单地等同于启动模式,这一块内容需要进一步验证。

(3)FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起出现,从上面的分析我们可以知道,singleTask启动模式默认就具有此标记位的效果。

(4)FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在XML中指定Activity的属性 android:excludeFromRecents = “true”。【无痕模式】


1.3 IntentFilter的匹配规则

        我们知道,启动Activity分为两种,显示调用和隐式调用。二者的区别这里就不多说了,显示调用需要明确地指定被启动对象的组件信息,包括包名和类名,而隐式调用则不需要明确指定组件信息。原则上一个Intent不应该既是显示调用又是隐式调用,如果二者共存的话以显示调用为主。显示调用很简单,这里主要介绍一下隐式调用。隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity。IntentFilter中的过滤信息有action、cateory、data。

1、action的匹配规则:action区分大小写,大小写不同字符串相同的action会匹配失败。

2、category的匹配规则:action是要求Intent中必须有一个action且必须能够和过滤规则中的某个action相同,而category要求Intent可以没有category【前提:隐式调用】;原因是系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”这个category,所以这个category就可以匹配前面的过滤规则中的第三个category。同时,为了我们的Activity能够接收隐式调用,就必须在Intent-filter中指定"android.intent.category.DEFAULT"这个category【也就是说,当隐式调用时,必须在声明Activity时加上android.intent.category.DEFAULT】。

3、data的匹配规则:

(1)data的数据格式:data有两部分组成,mineType和URL:


mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/* 等,可以表示图片,文本,视频等不同的媒体格式。

而URL【统一资源标识符】中包含的数据就比较多了:


(2)data的匹配规则:data的匹配规则和action类似,它也要求Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data,这里的完全匹配是指过滤规则中出现的data部分也出现在了Intent中的data中【也就是说,当在AndroidMenifest中声明Activity时指定了data,那么,在代码中隐式调用时必须匹配相应的data】。需要注意的是,虽然声明时过滤规则没有指定URI,但是却有默认值,URI的默认值为content和file,也就是说,虽然没有指定URI,但是Intent中的URI部分的schema必须为content或者file才能匹配。另外,如果要为Intent指定完整的data,必须要调用setDataAndType方法,不能先调用setData再调用setType,因为这两个方法彼此会清除对方的值。

(3)Intent-filter的匹配规则对于Service和BroadcastReceiver也是同样道理,不过系统对于Service的建议是尽量使用显示调用方式来启动服务。

(4)当我们通过隐式方式启动一个Activity的时候,可以做一下判断,看是否有Activity能够匹配我们的隐式intent,防止宕机,判断方法有两种:采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果它们找不到匹配的Activity就会返回null。

(5)不含有android.intent.category.DEFAULT这个category的Activity是无法接收隐式Intent的。

(6)在action和category中,有一类action和category比较重要:


1 0