Android发展也有十几年了。也许在很多Android开发者看来写Activity的文章已经意义不大。因为网络上已经也有太多太多这样的文章。此偏文章的目的是全面总结基础知识。将Acitivity中的生命周期,异常情况下的生命周期,启动模式,Flage,IntentFilter进行一个全面的总结,达到一篇文章全面理解所有Acitivity基础知识。
一、什么是Activity(活动):
活动可以通俗理解为我们看到的界面,其中可以设置可视化的布局,并响应我们和它的交互事件。
二、什么是生命周期?
对于一个页面来讲,它从开始创建到完全销毁是它的完整生命周期,其中会有许多方法会被在各个阶段调用执行,简称生命周期方法。
三、Activity的生命周期方法:
- onCreate:在Activity被创建的时候调用。其中会进行一些设置布局和初始化操作(如:点击事件)。
- onStart:表示Activity正在启动,此时的Activity已经可以被看见,但是还无法进行交互操作。
- onResume:表示Activity已经可被看见,并且已经可以进行交互。
- onPause:表示Activity正在暂停,处于不可交互,此方法中我们可以处理轻量级的工作,比如停止动画,和简单的数据存储,不能进行太耗时的操作,因为只有在这个方法执行完成之后,第二个Activity才会被创建并显示。
- onStop:表示Activity已经停止,完全可见状态,在第二个Activity的onResume:方法执行完成之后执行,此时的Activity可能因为系统内存不足被销毁。此方法中可以进行一些稍微重量级的回收工作,同样不能太耗时。
- onDestory:表示Activity正在被销毁,(不一定会调用。被系统内存不足销毁的时候不会调用此方法)我们可以在这里处理一些回收和最终的资源释放。
- onRestart:表示Acitivity从后台onStop状态回到前台,会在onStart方法前调用。
四、正常情况下的生命周期。
- 单个Activity的销毁到结束:我们创建一个MainActivity然后在上面的生命周期方法中打印log日志。我们开始运行,并使用back键进行销毁。
onCreate → onStart → onResume→ onPause→ onStop→ onDestroy - 两个Activity,MainActivity中创建一个Button用来打开SecondActivity。使用Back键销毁SecondActivity和MainActivity。
(M)onCreate → (M)onStart → (M)onResume→ 点击Button→(M)onPause→(S)onCreate → (S)onStart → (S)onResume→ (M)onStop→ 点击Back返回MainActivity→ (S)onPause→ (M)onRestart→ (M)onStart → (M)onResume →(S)onStop→(S)onDestroy→ 点击Back销毁MainActivity→(M)onPause→(M)onStop→(M)onDestroy
总结:当一个Activity跳转到另一个Activity时,总是先调用第一个Acitivity的onStop方法,然后等到第二个Activity执行完onResume方法之后,才继续往下执行自己的生命周期方法。 - 一个Activity和一个DialogActivity,MainActivity中创建一个Button用来打开ThirdDialogActivity。使用Back键销毁ThirdDialogActivity和MainActivity。
(M)onCreate → (M)onStart → (M)onResume→ 点击Button→(M)onPause→(S)onCreate → (S)onStart → (S)onResume→ 点击Back返回MainActivity→ (S)onPause → (M)onResume →(S)onStop→(S)onDestroy→ 点击Back销毁ThirdDialogActivity→(M)onPause→(M)onStop→(M)onDestroy
总结:当打开一个DialogActivity的时候,MainActivity处于可见不可交互状态,此时MainActivity的onPasue方法会执行onStop不会执行,当返回MainActivity的时候也只会执行onResume方法。 - Google文档表示,当Activity处于后台只时,当内存不足时可能会被销毁。并不会执行Activity的onDestory方法,当返回这个Activity时会调用onCreate方法。
五、非正常情况下的生命周期
- 当Activity横竖屏切换的时候,Activity会被销毁并重新创建。此时为异常生命周期调用,并且Activity生命周期会重新创建1次,而Fragment会被重新创建两次。
Activity被异常创建的原因是因为系统配置发生了变化。从横屏切换到了竖屏。因为配置发生了变化会去改变它要加载的很多资源,如横竖屏时系统会加载不同的布局文件。
如果想要Activity在横竖屏切换的时候不去重新创建Activity,在清单文件中设置如下配置,设置之后,onConfigurationChanged方法将会被调用。
targetSdkVersion <=12
android:configChanges="orientation"
targetSdkVersion >12
android:configChanges="orientation|screenSize"
-
资源内存不足的时候导致优先级低的Activity被杀死。
前台可见的Activity优先级是最高的,当Activity处于后台不可见的时候,即onStop,并未onDestory。系统在内存不足的情况下有可能杀掉这个Activity,后台进程中的Activity被杀掉的时候是不会调用其onDestory方法的,这个很难去模拟。 -
上边说过当系统配置发生改变之后,Activity会被重新创建。我们设置configChanges可以避免这种被重新创建。我们通过下面一张图片来了解这个属性中可以设置的值的含义,设置多个值的时候用" | ”相连接。
configChanges属性介绍
六、异常状态下的数据保存
- 通过上面的异常情况我们知道Activity可能会被销毁重新创建,当我们这个Activity中有一些用户输入的数据,在重新创建的时候不进行恢复的话,会给用户带来不好的体验。而在这种情况下,Google给我们提供了onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)这对方法供我们去使用。我们可以在onSaveInstanceState中通过Bundle来保存我们想要恢复的数据,在onRestoreInstanceState中进行恢复,其实恢复的地方还可以在onCreate方法中进行,onCreate方法提供了Bundle参数也可以用来恢复数据。记得判断Bundle是否为空。
注意:系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用。
onSaveInstanceState在onStop之前被调用,和onPause没有先后顺序,onRestoreInstanceState在onCreate之后被调用。 - 委托机制:当Activity被异常销毁之后,他会委托给Window保存数据,而Window会让我们的顶层容器DecorView去保存数据,而我们的DecorView,而我们的顶层呢个容器DecorView会委托我们的所有子View去保存我们的数据。而想要看我们每个子View都能自己保存什么样的数据,可以自己去我们的子View当中去查看onSaveInstanceState和onRestoreInstanceState的源码。
七、Activity的启动模式(launchMode)
了解启动模式之前我们先了解一个概念,任务栈,所谓任务栈,就像我们的手枪弹夹一样,我们把一颗颗子弹压入弹夹,当再想取出子弹的时候,总是把最后一个压入弹夹的子弹最先取出来,这就是一个任务栈,原则:先入后出。
而对于Activity来讲我们可以指定他的返回栈,通过xml中设置TaskAffinity属性,必须与包名不同。
我们可以使用 adb shell dumpsys activity查看Activity所属任务栈。
- standard(默认):
1、当B页面被A页面打开之后,B将会被创建放入A的返回站内,置于栈顶。无法指定TaskAffinity属性。
2、当启动B的页面为ApplicationContext的时候,将会报错,因为ApplicationContext没有所谓的返回栈,只有将B设置flage为FLAGE_ACTIVITY_NEW_TASK才行,此方法其实是把B设置为singleTask启动模式,会创建一个新的任务栈。 - singleTop(单一顶部);
1、当Acitivity 已经在栈顶的时候,将不会创建新的Activity实例。同时会调用他的onNewInstance方法。如果Activity已经不在顶部,将会重新创建事例放置在栈顶。
2、和standard一样此启动模式无法指定TaskAffinity属性,Acitivity都将放入在启动它的任务栈之内。 - singleTask(栈内复用模式):
1、当Activity没有指定taskAffinity属性的时候,Activity默认的任务栈为包名的启动栈,当被打开的时候,首先查看启动栈是否存在,如果存在,查看栈中是否有实例存在,如果存在,把栈中在Acitivity之上的所有实例清空,将Acitivity显示出来,并调用onNewIntent方法
2、当Acitivity指定taskAffinity属性,并且打开的singleTop、standard、任务栈相同的singleTask启动模式的Acitivity的时候,两个Acitivity都会在同一个栈中,并且,为前台任务栈。 - singleInstance:(单实例模式)
1、当Acitivity使用singleInstance,Acitivity会创建一个新的任务栈并且独享这个任务栈。再次打开他的时候会调用onNewIntent方法并不会重新创建。
2、此Acitivity打开新的standard、singleTop模式的Acitivity也不会将他们放在自己的任务栈中
3、singleInstance打开一个singleTask,两个Activity的TaskAffinity属性指定为一样。也不会放在同一个栈中。
4、singleInstance不指定TaskAffinity属性,也会创建一个新的任务栈。
八、Activity的Flags
flage不是启动模式,要将他们区分开。
- FLAG_ACTIVITY_NEW_TASK:会判断有没有他想要的任务栈(TaskAniffity)。如果没有创建他的要的任务栈并压入。如果有看是不是在栈顶,如果是在栈顶复用,调用onNewIntent,如果不是在栈顶,创建一个新的Activity压入栈。用在service或者广播打开Acitivity的时候用此标记。
- FLAG_ACTIVITY_SINGLE_TOP:和启动模式singleTop相同。
- FLAG_ACTIVITY_CLEAR_TOP:如果是默认模式启动的Acitivity设置此标记位,那么这个Activity和他之上的Activity都要出栈,并被重新创建放入栈顶。这个标记位和singleTop启动模式一起使用,其效果和singleTask启动模式一摸一样,只是无法设置TaskAffinity指定任务栈。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:标记这个标记的Acitivity不会出现历史Acitivity的列表中,不会后退回来。和xml中设置android:excludeFromRecents="true"一样
九、IntentFilter的匹配规则
- action:必须匹配的。只要有 就要一样,区分大小写,可以设置多个,匹配上一个就行。
- category:
1、首先必须有一个 <category android:name="android.intent.category.DEFAULT"/>,否则是接收不到隐式启动的。
2、Intent中如果有category,那就必须每个都要匹配的上。 - data:如果定义了data Intent中必须设置data 而且必须匹配的上。和action规则一样
<data android:scheme="string" 协议 如 http file content
android:host="string" 主机 如 www.baidu.com
android:port="string" 端口号:8080
android:path="string" 完整路径信息,不能使用 通配符*
android:pathPrefix="string" 完整路径信息,可以使用通配符*
android:pathPattern="string" 路径前缀信息
android:mimeType="sstringt" 数据类型 image/jpg等
/>
如果过滤规则中没有指定URI规则,但是URI却有默认值,file和content,我们的Intent的URI部分的scheme必须是content或者file才能匹配的上。
必须使用Intent.setDataAndType。因为setData 和setType会互相清空对方的值。
我们可以使用PackageManager提供的两个方法来验证我们的intent是否能匹配到Activity 防止我们打开Acitivty的时候出现找不到的异常。
resolveActivity 和queryIntentActivities 他们的参数中flage我们设置为MATCH_DEFAULT_ONLY。标记此是为了将有default的category的返回给我们,因为如果没有这个category,Activity将不会收到隐式调用。resolveActivity 返回的是最佳匹配的集合 queryIntentActivities 返回的是所有能匹配上的集合。
网友评论