第一章Activity的启动与生命周期
正常情况下的启动状态
onCreate加载布局,初始化数据
onStart界面可视化,但尚在后台,不可交互。
onRestart从不可见(onStop)到可见(onStart)需要经过onRestart
onResume从后台到前台,Activity变得可以交互。
onPause一般onPause后紧接着onStop,所以不推荐将耗时的操作放在这里。
×当新的Activity为透明时,原先Activity还是可视的,因而不会执行onStop
onStop从不可互动到不可视,如点击home键回到桌面,这是Activity一般还没有被销毁,等待着onRestart->onStart->onResume重新恢复状态
onDestroy销毁Activity,并进行一些回收工作,如关闭数据库等。
一张应该出现过很多遍的图
基于书上说的屏幕的点亮熄灭以及手指的触摸,我个人试了一下
界面一切·从简,就是默认emptyActivity的界面
每个方法写都写一个Log.d("ActivityLife","onXXXX");//XXXX视方法而定
Activity刚启动时: onCreate->onStart->onResume
启动时
点击返回键,关闭程序时:onPause->onStop->onDestroy
退出
点击home键:onPause->onStop
后台
从后台唤醒:onRestart->onStart->onResume
唤醒
一直到这里,都和我们在前面的理论是一样的。
锁屏时,其实和挂到后台是一样的
锁屏
当唤醒屏幕时,如果有密码的话,唤醒之后是要输入密码,这个时候Activity的界面依旧不可见,因此不会调用以上的任何方法,只有解锁后,Activity重新可见时,又会执行之前从后台唤醒相同的步骤
唤醒
从Activity1启动Activity2,会先执行Activity1的onPause,然后创建Activity2(onCreate->onStart->onResume)再执行Activity1的onStop.
一些小细节关于生命周期的重点在于onStart、onStop控制的是Activity是否可见(在后台),onResume、onPause控制的是Activity是否在前台可供操作。
异常情况下的生命周期
而onSaveInstanceState是系统自行销毁Activity时对数据的保存。重新打开时调用onRestoreInstanceState恢复数据。即异常情况下销毁Activity时,会调用这两个方法恢复数据。而横竖屏就属于这种异常情况。
横竖屏
- 这里我们可以发现,onSaveInstanceState是在onStop前执行的,而onRestoreInstanceState是在onStart后执行的。
- 当Activity异常情况下的情况下需要重新创建时,系统会默认我们保存当前Activity的视图结果,并在Activity重启后为我们恢复这些数据,比如文本框用户输入的数据,ListView的滚动位置等
- 关于保存和恢复View的层次结构,系统的工作流程:首先Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window会委托它上面的顶级容器去保存数据,顶层容器一般是DecorView(ViewGroup);最后顶层容器再去一一通知它的子元素保存数据,这样整个数据的保存过程就完成了。
×这里还要注意一下,onSaveInstanceState还有一个两个参数的方法,是不会在异常状态中调用的。
资源不足导致的Activity被销毁
Activity的优先级
- 正在和用户交互的Activity优先级最高: onResume
- 可见但非前台的Activity优先级次之:onPause
- 后台Activity优先级最低:onStop
当系统内存不足时会杀死目标Activity的进程,再用onSaveInstanceState和onRestoreInstanceState进行恢复。
如果一个进程在后台没有四大组件在运行,很容易被系统回收,因此可以在后台放入一个service提高优先级。
configChanges
当系统配置发生改变时,Activity会重新创建,如果在Activity指定configChanges属性,Activity就会执行configChanges的属性,而不用重新创建了。
如横竖屏切换:
manifests中配置
android:configChanges = "orientation"
//多个属性用|
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d("ActivityLife","onConfigChanged:"+newConfig.orientation);
}
Activity创建后,从竖屏到横屏再到竖屏的过程中
没用调用onSaveInstanceState和onRestoreInstanceState方法,也没有重新创建Activity configChanges的一些属性
常用的是locale、orientation和keyboardHidden
×android4.0以上orientation要注意配置screensize,不然还是会调用onCreate
使用configChanges:orientation时,就不会使用oSaveInstanceState和onRestoreInstanceState来恢复数据了,因为都没有销毁。。。
Activity的启动模式
Activity的LaunchMode
任务栈是一个“后进先出”的栈结构,每次finish()处于前台的Activity就会出栈,直到栈为空为止,当栈中无任何Activity的时候,系统就会回收这个任务栈。
-
standard
默认的模式,每次启动都会创建一个新的Activity到任务栈中。Activity A启动Activity B会让B进入A所在的任务栈中,就是这种特性导致,ApplicationContext没有任务栈,context启动standard的Activity时会发生错误。这时候需要在Intent中加入FLAG_ACTIVITY_NEW_TASK,这时实际上是以singleTask启动的。
Actvity1启动Activity1
销毁上面的Activity1
-
singleTop
栈顶复用模式,若该Activity处于任务栈的栈顶,那么就不会重新创建该Activity。
(Activity1为singleTop)
Activity1启动Acticity1时:
只调用onPause->onNewIntent->onResume
销毁时:
只有单个Activity在栈中
Activity1启动Activity2再启动Activity1时:
捕获.PNG
会重新创建Activity1
销毁时:
销毁最上面的Activity1
销毁Activity2 -
singleTask
栈内复用模式。这种模式下,Activity在一个栈中存在,那么多次启动该Activity都不会重新创建实例,和sinleTop一样,系统会回调其onNewIntent。如果启动的Activity没有所需要的任务栈,就会先创建任务栈再创建Activity。singleTask默认具有clearTop的效果,具有该模式的Activity会让其之上的Activity全部出栈。
Activity1处于栈顶时启动Activity1
这里和singleTop是一样的
Activity1启动Activity2再启动Activity1
注意这里,由于不会生成新的Activity1,所以调用onRestart->onStart->onResume,而Activity2会直接被销毁。 - singleInstance
单实例模式。这是一种加强的singleTask模式,除了具备singleTask的特性之外,具有该模式的Activity只能单独位于一个任务栈中;比如Activity A是singleInstance模式的,当A启动后,系统会为它创建一个新的任务栈,后续的启动均不会创建新的Activity,除非这个任务栈被系统销毁了。
这里可以这么理解,singleInstance会创建一个新的任务栈,这个任务栈中只有自己一个实例,启动其他Activity会重新创建一个任务栈,供其他的Activity使用。此时Activity的销毁顺序与创建顺序无关,和任务栈内Activity的顺序有关。当销毁时处在singleInstance所在的任务栈,那么该任务栈会被销毁,跳转到另一个任务栈的栈顶,再根据这个任务栈Activity的次序进行销毁。而当销毁工作处于另一个任务栈时,会先选择清空这个任务栈的所有实例,在跳转到唯一的singleInstance所在任务栈的实例。
关于四个启动模式的流程图->http://www.jianshu.com/p/7a11ff292423
LaunchMode的使用方法
在manifests中activity下
android:launchMode="singleInstance"
或者在intent中addFlag
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
这里第二种方法的优先级更高,而区别在于第一种方法不能使用FLAG_ACTIVITY_CLEAR_TOP,第二种不能设置singleInstance。
这里书中还提到了TaskAffinity和AllowTaskReparenting两个属性
TaskAffinity标识了Activity所需的任务栈的名字
AllowTaskReparenting="true"时,例子如下:
A启动B的ActivityC,ActivityC刚好是这个属性,此时C会在A的任务栈中。当按下home键再打开应用B时,会直接打开ActivityC,而不是默认的首页面。此时ActivityC会从A的任务栈转移到B的任务栈
adb shell dumpsys activity命令可以详细的了解当前任务栈情况
Activity的Flags
- FLAG_ACTIVITY_NEW_TASK
即singleTask
- FLAG_ACTIVITY_SINGLE_TOP
即singleTop
- FLAG_ACTIVITY_CLEAR_TOP
有此标记的Activity在启动时,在其上的所有Activity都将被销毁,一般会和singleTask一同出现,当被启动的Activity是standard模式时,该Activity及以上的Activity都会出栈,再重新创建新的Activity
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
有该标识的Activity将不会出现在历史Activity列表中,xml中等同于:
android:exculdeFromRecents="true"```
#####IntentFliter的匹配规则
- 显式调用
明确表示启动对象的包名类名
- 隐式调用
需要Intent匹配IntentFliter所设置的过滤信息
- 一个Activity可以有多个IntentFliter,一个IntentFliter可以有多个action、category或是data。
- 其中Intent需要同时匹配一个IntentFliter中的三种(action、category和data)信息。
- 一个Activity中只要匹配一个IntentFliter就可以了。
- #####Action
action系统有预定义的,当然我们也能自己定义,其中intent中action必须与IntentFliter中一个action匹配。(没有就默认不匹配)还有一点就是action区分大小写
- #####Category
category系统有预定义的,当然我们也能自己定义。category与action不同的是:intent中没有category是默认匹配成功的。如果intent定义了category,则要求intent中所有的category都是intentFliter的category中的一个。
- #####data
data的匹配模式与action类似,都需要至少一个匹配。
data的语法
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
data由两部分组成,URI和mimeType。URI指定类似于路径的状态,mimeType指定媒体数据
- URI的结构有mimeType前面的scheme、host、port、path、pathPattern、pathPrefix组成。
<scheme>://<host>:<port>/{<path>|<pathPrefix>|<pathPattern>}
对应:
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
- scheme:URI的模式,如http、file、content等,若没有指定scheme,URI是无效的。
- Host:URIde主机名,URI中不能为空
- Port:URI的端口号,scheme和host都指定了才有意义。
- Path、pathPattern、pathPrefix:三个参数表述路径信息。
Path指定完整路径
pathPattern也指定完整路径,但是可以使用通配符"",""表示0或多个字符。由于正则表达式的规范,真实字符串中""要用"\"表示,""要用"\\"表示
pathPrefix表示路径的前缀信息
当没有指定URI时,URI默认为content和file。
Intent添加data信息:
如果要指定完整data信息,需要setDataAndType
intent.setDataAndType(Uri.parse("file://abc"),"image/png");
使用setData和setType会彼此擦除对方的值
这里data比action特殊的还有一点,就是:
<intent-filter...>
<data android:scheme="file" android:host="www.baidu.com"/>
...
</intent-filter>
<intent-filter...>
<data android:scheme="file"/>
<data android:host="www.baidu.com"/>
...
</intent-filter>
上面的两种方法是一样的。
当我们隐式启动一个Activity的时候,可以做一下判断,看是否能匹配到我们的隐式Intent,如果不做判断没找到对应的Activity系统就会抛出android.content.ActivityNotFoundException异常。
采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null,我们通过判断返回值就可以规避上述错误了。
######接收隐式意图的Activity必须有<category android:name="android.intent.category.DEFAULT"/>,这个category的作用在于上述两个方法只要不返回null,就能启动Activity。
######有一类action和category的共同作用是标明这是一个入口Activity,并且会出现在系统的应用列表中,少一个都没有任何意义,也不会出现在系统的应用列表中。
<action android:name="android.intent.action.MAIN" />
//最先启动的activity
<category android:name="android.intent.category.LAUNCHER" />
//桌面的图标,显示在程序列表中
####知识点总结的比较全,难听点就是笔记太啰嗦了吧,感觉都有点重要,整章的内容都快要都写进来了。
####这一章的内容不是特别多,但我还是写的有些杂乱,或许写多了,就好了吧
网友评论