1.生命周期
典型情况下生命周期分析
1.onCreate:Activity正在被创建,可以在其中做一些初始化工作
2.onRestart:Activity正在被重新启动,一般是从不可见状态转换到可见状态
3.onStart:Activity正在被启动,即将开始,此时Activity已经可见,但还不能和用户交互,可理解成活动已经显示但看不到
4.onResume:Activity已经可见并且开始活动,此时活动才显示到前台
5.onPause:Activity正在停止,正常情况下会接着调用OnStop,若此时快速回到当前活动会调用onResume
6.onStop:Activity即将停止,可做稍微重量级的回收工作
7.onDestory:Activity即将销毁,Activity中的最后一个回调,做一些回收工作和最终的资源释放
正常情况
1.第一次启动Activity:onCreate->onStart->onResume
2.打开新Activity/切换回桌面:onPause->onStop,如果采用透明主题,不会回调onStop
3.再次回到原Activity时,onRestart->onStart->onResume
4.back键回退时,onPause->onStop->onDestroy
5.活动被回收后再打开时回调过程和1一致
6.从生命周期来看,onCreate和onDestroy配对,从是否可见来说,onStart和onStop从是否在前台来说,onResume和onPause是配对的
7.假设当前Activity为A,此时用户打开B,栈顶的Activity需要先onPause,新Activity才能启动
异常情况
1.资源相关的系统配置发生改变导致Activity被杀死并重新创建
首先粗略了解下系统的资源加载机制,把图片等资源放入drawable的各种目录下,我们可以通过Resoures去获取这些资源,当应用程序启动时,系统会根据当前设备情况去加载合适的Resources资源,比如当横竖屏状态改变时,默认情况下活动会被销毁并且重新创建,但我们也可以阻止活动的重建。
当系统配置发生改变时,Activity会被销毁,由于Activity是在异常情况下中止,系统会调用onSaveInstanceState来保存当前活动状态,调用时机在onPause前也可以在其之后,但它只会出现在活动被异常终止的情况下,正常情况下不会回调,活动被重新创建后,系统会调用onRestoreInstanceState,并把活动销毁时onSaveInstanceState保存的bundle对象传给onRestoreInstanceState和onCreate函数。(注意:onRestoreInstanceState一旦被调用,其参数一定有值,不用额外判空,而onCreate不一定,官方建议采用onRestoreInstanceState恢复数据)所以我们可以通过这两个方法来判断活动是否被重建,如果被重建就可以去除之前保存的数据并恢复,从时序上说onRestoreInstanceState的调用时机在OnStart之后。而且,系统在异常情况下会保持当前活动视图结构,并且在活动重启后为我们恢复数据,比如文本框中内容,listview滚动位置等。
保存view层次结构:活动被意外终止时,(正常销毁时不会,因为不可能再次被显示)系统调用onSaveInstanceState保存数据,然后活动会委托Window去保存数据,Window委托它上面的顶级容器(一个ViewGroup,一般来说是DecorView)去一一通知子元素保存数据。
2.资源内存不足导致低优先级的活动被杀死
优先级从高到低:前台活动->可见但非前台活动->后台活动
一个进程没有四大组件在执行,那么这个进程很快被杀死,一些后台工作放入服务中保证进程有一定的优先级,不会被轻易地杀死
系统配置发生改变时不重新创建活动的方法:
2.活动的启动模式
1.standard模式:每次启动一个活动都会重新创建一个新的实例,不管实例是否存在,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。谁启动了这个活动,那么这个活动就运行在启动他的那个活动的栈中,比如活动A启动了活动B,B就会进入A所在的栈中。当我们使用ApplicationContext去启动该模式下的活动时会报错,因为非Activity类型的ApplicationContext没有任务栈,可以为该活动指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动时会为它创建一个任务栈,此时待启动的活动实际上以SingleTask模式启动。
2.singleTop模式:栈顶复用模式,若新活动已经位于任务栈的栈顶,那么该活动不会被重新创建,而是回调newIntent方法,我们也可以通过此方法的参数去取出当前请求的信息,此时该活动的onCreate,onStart不会被调用,但如果它不在栈顶,那么就会重新创建
3.singleTask模式:栈内复用模式,这是一种单实例模式,只要存在就不会重新创建,同样也会回调newIntent。该模式活动启动后,先寻找有无对应的栈,没有则创建并入栈,有且没有实例时直接入栈,有实例时把实例调到栈顶,当初在其之上的会被出栈,直到该实例到达栈顶为止
4.singleInstance模式:加强版singleTask,具有其所有特性,并且此种模式的活动只能单独地位于一个任务栈中
活动所需任务栈的含义
TaskAffinity参数标识了一个活动所需要的任务栈的名字,默认情况下所有活动所需的任务栈名字为应用的包名,它主要和singleTask和allowTaskReparenting属性配对使用。任务栈可分为前台和后台,后台任务栈的活动处于暂停状态,可通过切换把后台任务栈调到前台。 1.当TaskAffinity和singleTask启动模式配对使用的时候,他是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。 2.当TaskAffinity和allowTaskReparenting结合的时候,比较复杂,当应用A启动了应用B的某个活动时,如果这个活动的allowTaskReparenting为true时,那么当B被启动后,该活动会直接从应用A的栈转到应用B的任务栈中。当用A启动B的某个活动C后,此时按home键返回桌面再打开应用B,这个时候显示的是被A启动的活动C,也就是说C从A的任务栈去到了B中,因为按我们之前的理解,A启动了C,那么C肯定在A的任务栈中,但是C的TaskAffinity肯定和A中的不同,所以它会创建自己的任务栈,当我们返回桌面后启动应用B,这个时候系统发现C原本需要的任务栈已经被创建了,就把C从A抢过来了
如何指定启动模式
1.在AndroidMenifest中为活动指定(无法直接为活动设定FLAG_ACTIVITY_CLEAR_TOP标识)2.在Intent设置标志位(无法设置singleInstance模式)
Activity常用的Flags
1.FLAG_ACTIVITY_NEW_TASK:指定singleTask模式
2.FLAG_ACTIVITY_SINGLE_TOP:指定singleTop模式
3.FLAG_ACTIVITY_CLEAR_TOP:当有该标记位的活动启动时,同一任务栈中所有位于它上面的活动都要出栈,如果已经存在实例,系统会调用NewIntent,如果被启动的活动使用standard模式,那么连同它以及它之上的活动都要出栈,系统创建新实例放入栈顶。
4.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:该标记下的活动不会出现在历史活动列表当中,当某些情况我们不希望用户通过历史列表返回我们的活动时使用
IntentFilter的匹配规则
显式调用:需要明确指定被启动对象的组件信息
隐式调用:Intent能够同时匹配目标组件的的IntentFilter中所设置的过滤信息,三种类别同时匹配才算完全匹配
过滤信息列表:action category data
action的匹配规则
Intent必须存在且必须和过滤规则中的一个action相同,区分大小写
category的匹配规则
Intent中如果有category,必须和规则中的一个相同,Intent也可以没有category,因为系统在启动活动时会自动带上android.intent.category.DEFUALT,为了能隐式调用,必须在过滤器中指定android.intent.category.DEFUALT这个category
data的匹配规则
Intent必须存在且完全匹配其中一个
data的结构:mimeType(媒体类型:image/jpeg audio等)和URI
URI结构:<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
eg:content://com.example.project:200/folder/subfolder/etc
scheme:URI的模式,比如http,file,content等,如果URI没有指明模式那么整个URI无效
host:主机名,若没有指明同样整个URI无效
port:端口号,仅当URI指定了模式和主机名时才有效
path,pathprefix,pathpattern:路径信息,path和pathpattern都表示完整的路径信息,但是后者可以包含分配符,*
表示0或多个任意字符,由于正则表达式的规范*
要写成\\*
,\
要写成\\\\
。pathprefix表示路径的前缀信息
eg:当过滤器指定媒体类型为image/*
时,Intent中也要设置成image/*
才能匹配,虽然过滤规则没有指定URI,但默认为content和file,所以Intent中也要指定其中一个。而且要指定完整的data必须调用setDataAndType方法,setData和setType都会清除另一个类型的值。Intent中设置的uri要一一满足过滤器中的uri结构的细分类型。
注意:通过隐式调用去启动活动时,可以用PackageManager的resolveActivity或者Intent的resolveActivity做一下判断,看看是否有活动能匹配我们的Intent,PackageManager的queryIntentActivity可以返回所有成功匹配的活动信息,这些函数的第二个参数是一个标记位,使用MATCH_DEFAULT_ONLY的含义是匹配那些在过滤器中声明了category.DEFAULT的活动,意义在于这两种方法都不返回null是一定成功启动活动,因为不含default的活动是无法使用隐式调用的
比较重要的标记位
1.action.MAIN和category.LAUNCHER:共同标明一个入口活动,缺一不可