很多的内容都是来自于”Android开发艺术探究”这本书,希望写完这篇笔记。可以全方面的了解Activity。
一,Activity的生命周期
这里Activity分为两种情况考虑:
①典型情况下的Activity ,用户正常操作的情况。
②异常情况下的Activity ,Activity被系统回收,或者由于一些缘故被销毁重建。
1,典型情况下的Activity的生命周期
进入A活动:
A: onCreate
A: onStart
A: onResume
从A活动跳到到B活动:
A: onPause
B: onCreate
B: onStart
B: onResume
A: onStop
从B返回A:
B: onPause
A: onRestart
A: onStart
A: onResume
B: onStop
B: onDestroy
onCreate,Activity第一个执行的方法,表示创建Activity。setContentView加载布局资源都在这里进行。
onRestart,Activity被重新启动。比如上面的B回退到A,A从不可见到可见时会调用。
onStart,Activity获得了焦点,获得焦点的意思是用户可以看见,但是无法和用户交互,比如无法响应用户的点击等。
onResume,Activity变得可见。
onPause,失去焦点。
比如在A活动中弹出一个透明主题的Dialog或Activity,A活动会失去焦点,会执行onPause。但不会执行onStop。
onStop,用户不可见。
锁屏和回到桌面,Activity会不可见,会触发onStop执行。
onPause和onStop不可以执行耗时的操作,因为会影响新的Activity的启动。
onDestroy,Activity被销毁。
onCreate和onDestroy只可以被执行一次。
2,异常情况下Activity的生命周期
①横竖屏切换时导致的Activity销毁重建
②资源不足导致的优先级低的Activity被杀死
2.1横竖屏切换时导致的Activity销毁重建
进入A活动:
A: onCreate
A: onStart
A: onResume
A活动横竖屏切换
A: onPause
A: onSaveInstanceState
A: onStop
A: onDestroy
A: onCreate A: onStart A: onRestoreInstanceState A: onResume
A活动销毁被重建。这种情况属于Activity在异常情况下被终止,系统会调用onSaveInstanceState方法用来保存当前Activity的状态数据,当该Activity重新被创建时,则会调用onRestoreInstanceState,把保存当前Activity的状态通过Bundle传递过来。
onSaveInstanceState和onRestoreInstanceState被调用只会出现在Activity异常终止时会调用,正常情况不会被调用。
可以利用Bundle把销毁前的CheckBox的状态记录下来。
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("box",box.isChecked());
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
box.setChecked(savedInstanceState.getBoolean("box"));
super.onRestoreInstanceState(savedInstanceState);
}
避免横竖屏导致的Activity重建,有两种方法:
第一种,让Activity的屏幕方向锁定。
android:screenOrientation="portrait" 竖直方向
android:screenOrientation="landscape" 水平方向
第二种,为Activity指定configChanges属性。
android:configChanges="orientation"
2.2资源不足导致的优先级低的Activity被杀死
Activity的优先级:
最高的:就是和用户正在交互的Activity
其次:比如弹出一个对话框或透明的Activity,失去焦点的Activity
最后:回到桌面或锁屏的Activity,此时已经不可见系统内存不足时,
就会按照上面的优先级去杀死该Activity所在的进程。
而如果一个Activity没有四大组件,将会被很快杀死。
二,Activity的启动模式
为什么会有多种Activity的启动模式?
每跳到一个Activity,系统就会创建它的实例把该Activity放入堆栈。按回退键,Activity会出栈。而如果误操作多次的开启同一个Activity,按照这种堆栈结构,要不断的回退才能恢复到最初。默认的启动模式很呆板,所以就有了多种启动模式。
(1)standard
默认的标准模式,每次启动Activity都会创建其实例。
A,B均是standard模式下
A--->B B活动会进入到A活动所在的栈中。
B--->B 会重新创建B的实例,进入到堆栈中
此时的堆栈情况是ABB。
(2)singleTop
栈顶复用模式,当新的Activity位于栈前,那么该Activity不会被重新创建。
B为singleTop模式下
如果目前的栈内是AB,此时要跳到B
AB--->B,此时的栈内依然是AB。而B活动的onNewIntent方法会被回调。
(3)singleTask
栈内复用模式,这种模式下的Activity,只要在栈内存在,多次启动此Activity也不会创建实例。
D为singleTask模式
假如此时的栈内有ABC三个活动,栈的名字为task1。
ABC--->D 如果D活动所需的栈是task2,首先会先创建task2的任务栈,把D的实例压入task2中。
而D活动所需栈是task1,那么把D压入到task1的任务栈,此时栈内就是ABCD。
B为singleTask模式
假如此时的栈内有ABC三个活动,栈的名字为task1。
ABC--->B 如果B活动所需的栈是task2,首先会先创建task2的任务栈,把B的实例压入task2中。
如果B活动所需栈是task1,由于task1栈内已经有了B实例,所以不会创建其实例。
而singleTask的模式默认具有clearTop的效果,会把栈内B以上的Activity移出栈,此时的栈内是AB。
(4)singleInstance
单实例模式,具备此模式的Activity只能单独的存在于一个栈内。
A活动跳到singleTask模式的B,可以打印每个Activity的getTaskId()的值,会发现无论怎么样,没有一个Activity的taskId和B是一样的。
三,IntentFilter的匹配
众所周知,Activity的启动,可以是显示调用也可以是隐式调用。
显示调用指明需要启动的Activity的在那个包,类名是什么。而隐式调用则需要intent可以匹配Intent-Filter中所设置的过滤信息,匹配才可以被调用起来。
Intent-Filter中的例子:
<intent-filter>
<action android:name="myaction"/>
<category android:name="mycagetory"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="mimeType"/>
</intent-filter>
action:
系统有自己定义的action,同样我们也可以自己定义。
在调用activity时,intent的action必须存在且和过滤规则中的任意一个action相同就可以匹配上。
如果过滤规则中没有指定action,那么在intent中指定action会报错。
同样在过滤规则中指定了action,但intent中不指定,也会出现问题。
category:
系统也同样有自己的category,我们也可以自定义。
同样的,只要intent中有category和过滤规则中的category有一个相同,就能匹配上。不过有一个特别重要的一点:
在startActivity和startActivityForResult都会为intent添加上一个默认的category:android.intent.category.DEFAULT。
所以在过滤规则中必须要加上
<category android:name="android.intent.category.DEFAULT"/>
否则怎么也匹配不上。因为过滤规则中没有,而你添加了,那么怎么也都不能把activity调起来。
data:
<data
android:scheme=""
android:host=""
android:port=""
android:path=""
android:pathPattern=""
android:pathPrefix=""
android:mimeType="mimeType"
/>
data总共由两部分组成,一个是mime类型和URI。
mime类型指媒体格式,比如image/jpeg,audio/mpeg4-generic和video/*,可以表示图片,视频等不同的媒体格式。
URI的格式:
http://192.168.1.100:8080/flight/index.hmtl
其中scheme表示URI的模式,比如上面是http,还可以是content,flie等。如果没有指定scheme,那么整个URI是无效的。
host表示URI的主机名
port表示URI的端口
path,pathPattern,pathPrefix表示路径信息,path表示完整的路径信息,而pathPattern也可以表示完整的路径信息,但可以使用通配符“*”来表示任意的字符。由于正则表达式的规范
“*”要写为“\\*”
"\"要写为"\\\\"
而pathPrefix表示的是路径的前缀。
<intent-filter>
..........
..........
<data
android:mimeType="image/*"
/>
</intent-filter>
要怎么匹配上面的过滤规则?
上面的过滤规则并没有指定URI的值,但是URI是有默认值的,默认值为content和file。
所以要写为:
intent.setDataAndType(Uri.parse("file://xxx"),"image/png");
或者
intent.setDataAndType(Uri.parse("content://xxx"),"image/png");
但不可以这样写:
intent.setData("file://xxx");
intent.setType("image/png");
原因是setData中会将mineType置为null。而同样setType中会将URI置为null。
判断隐式意图开启activity是否成功,可以使用PackageManager的resolveActivity方法
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
当返回的是null,表示启动失败。其中传入的flags要使用MATCH_DEFAULT_ONLY。它表示仅仅匹配那些在intent-fliter中声明了默认的category的Activity。
因为activity不声明默认的category,这个activity是无法被隐式意图调用起来的。