部分内容从这个里面拷贝,注明地址:http://hujiaweibujidao.github.io/blog/2015/11/29/Art-of-Android-Development-Reading-Notes-1/
1.1 Activity生命周期全面分析
1.1.1 典型情况下生命周期分析
(1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart
方法就会被调用。
(2)当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause
-> onStop
,但是如果新Activity采用了透明主题,那么onStop
方法不会被回调。当用户再次回到原来的Activity时,回调如下:onRestart
-> onStart
-> onResume
。
(3)onStart
和onStop
对应,它们是从Activity是否可见这个角度来回调的;onPause
和onResume
方法对应,它们是从Activity是否位于前台这个角度来回调的。
(4)从Activity A进入到Activity B,回调顺序是onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A)
,所以不能在onPause方法中做重量级的操作
(1)资源相关的系统配置发生改变导致Activity被杀死并重新创建
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestrory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态,这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,它既可能在onPause之前调用,也可能在onPause之后调用,需要强调的一眯是这个方法只会在activity被异常终止的情况下,正常情况下系统不会回调这个方法。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法,因此,我们可能通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceStatte的调用时机在onStart之后。新的activity调用时序----->onCreat--->onStart--->onRestoreInstanceState----onResme---onPasuse----onStop---OnDesotry
当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、listview滚动的位置等,这些view相关的状态系统都会默认为我们恢复。具体针对某一个view系统能为我们恢复哪些数据可以查看view的源码中的onSaveInstanceState和onRestoreInstanceState方法
系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。”这句话的描述不确切,准确点说,应该是“系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用
(2) 资源内存不足导致底优先级的Activity被杀死
activity按照优先级从高到底分为三种:
1、前台Activity-----优先级最高,正在和用户交互的Activity
2、可见但是非前台Activity---------比如activity中弹出了一个对话框
3、后台activity--------执行了onstop优先级最低
当系统内存不足就会按优先级去杀死activity所有的进程,通过指定activity的configChange来让activity屏幕方向更改不重新创建,具体有哪些值参考网上或者是书上,屏幕更改的时候onConfigurationChanged会调用。
android:configChanges="xxx"
属性,常用的主要有下面三个选项:
local
:设备的本地位置发生了变化,一般指切换了系统语言;
keyboardHidden
:键盘的可访问性发生了变化,比如用户调出了键盘;
orientation
:屏幕方向发生了变化,比如旋转了手机屏幕。
配置了android:configChanges="xxx"
属性之后,Activity就不会在对应变化发生时重新创建,而是调用Activity的onConfigurationChanged
方法。
1.2 Activity的启动模式
更具体的查看:http://blog.csdn.net/lijinhua7602/article/details/486383171.2.1 activity的launchMode是一种后进先出的栈结构,当任务栈中没有任何Activity的时候,系统就会回收这个任务栈
(1) standard;标准模式,当我们用ApplicationContext去启动standard模式的activity会报错,因为standard模式的activity默认会进入启动它的actiivty所属的任务栈,由于非activity类型的context并没有所谓的任务栈,从非Activity类型的Context(例如ApplicationContext、Service等)中以standard
模式启动新的Activity是不行的,因为这类context并没有任务栈,所以需要为待启动Activity指定FLAG_ACTIVITY_NEW_TASK
标志位,这样启动的的时候就会为它创建一个新的任务栈,这个时候待启动activity实际上是以singleTask模式启动的,后面会讲解到。
(2) singleTop:栈顶复用模式,如果新的actiivty已经在任务栈的栈顶就不会重新创建,同时它的onNewIntent方法就会被调用,我们可能通过这个方法取出参数,其他的onCreat不会调用,如果新的要启动的activity不是在栈顶还是会创建
(3)singleTask:栈内复用模式----这是一种单实例模式,singleTask模式的具体分析:当一个具有singleTask启动模式的Activity请求启动之后,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建Activity的实例把它放到栈中;如果存在Activity所需的任务栈,这时候要看栈中是否有Activity实例存在,如果有,那么系统就会把该Activity实例调到栈顶,并调用它的onNewIntent方法(它之上的Activity会被迫出栈,所以singleTask模式具有FLAG_ACTIVITY_CLEAR_TOP效果);如果Activity实例不存在,那么就创建Activity实例并把它压入栈中
(4) singleInstance:单实例模式,比如Activity A是singleInstance模式,当A启动过后系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内利用的特性,后续的均不会创建新的activity,除非这个任务栈被系统销毁了。
TaskAffinity可以翻译为任务相关性,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有的Activity所需的任务栈的名字为应用包名,我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义
参考:http://blog.csdn.net/zhangjg_blog/article/details/10923643
当TaskAffinity和allowTaskReparenting这个情况比较复杂,
设置启动模式既可以使用xml属性android:launchMode
,也可以使用代码intent.addFlags()
。区别在于限定范围不同,前者无法直接为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识,而后者无法为Activity指定singleInstance模式
1.2.2 Activity的Flags
FLAG_ACTIVITY_NEW_TASK : 这个标记位的作用是为Activity指定"singleTask"启动模式,其效果和在XML中指定该启动模式相同
FLAG_ACTIVITY_SINGLE_TOP : 这个标记的使用是为Activitty指定"SingleTop"启动模式,其效果和在XML中指定该启动模式相同
FLAG_ACTIVTIY_CLEAR_TOP : 具有此标记的Acivity,当它启动时,在同一个任务栈中所有位于在上面的Activty都要出栈,这个模式一般需要和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况activity已经存在,系统会调用onNewintent,在这之上的都要出栈。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有这个标记和Activtiy不会出现在历史的Activity的列表中,等同于Activity的属性android:excludeFromRecents="true"
1.3 IntentFilter的匹配规则
启动Activity分为两种,显示调用和隐式调用 (1)IntentFilter中的过滤信息有action、category、data,为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别、category类别和data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。此外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intenf-filter即可成功启动对应的Activity。
<intent-filter> |
(2)action匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action,只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,action匹配区分大小写。即上面的只要我们的Intent中的action值为"com.ryg.charpter_1.c"或者"com.ryg.charpter_1.d"都能匹配成功。如果没有指定action,则会匹配失败。(3)category匹配规则
Intent中如果有category那么所有的category都必须和过滤规则中的其中一个category相同,如果没有category的话那么就是默认的category,即android.intent.category.DEFAULT
,所以为了Activity能够接收隐式调用,配置多个category的时候必须加上默认的category。
(4)data匹配规则
data的结构很复杂,语法大致如下:
<data android:scheme="string" |
主要由mimeType
和URI
组成,其中mimeType代表媒体类型,而URI的结构也复杂,大致如下:
<scheme>://<host>:<port>/[<path>]|[<pathPrefix>]|[pathPattern]
例如content://com.example.project:200/folder/subfolder/etc
scheme、host、port
分别表示URI的模式、主机名和端口号,其中如果scheme或者host未指定那么URI就无效。
path、pathPattern、pathPrefix
都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面包含了通配符(*),"*"表示0个或者多个任意字符,需要注意的是由于正则表达式的规范,如果想表示真实的字符串,那么"*"要写成"\\*";
data匹配规则:Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。
URI有默认的scheme!
如果过滤规则中的mimeType指定为image/*
或者text/*
等这种类型的话,那么即使过滤规则中没有指定URI,URI有默认的scheme是content和file!如果过滤规则中指定了scheme的话那就不是默认的scheme了。
//URI有默认值 |
如果要为Intent指定完整的data,必须要调用setDataAndType
方法!
不能先调用setData然后调用setType,因为这两个方法会彼此清除对方的值。
intent.setDataAndType(Uri.parse("file://abc"), "image/png"); |
data的下面两种写法作用是一样的:
<intent-filter> |
如何判断是否有Activity能够匹配我们的隐式Intent?
(1)PackageManager
的resolveActivity
方法或者Intent的resolveActivity
方法:如果找不到就会返回null
(2)PackageManager的queryIntentActivities
方法:它返回所有成功匹配的Activity信息
方法原型:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,int flags);
public abstract ResolveInfo resolveActivity(Intent intent,int flags);第二个参数需要注意,我们要使用MATCH_DEAFAULT_ONLY这个标记,这个标记位的含义是仅仅匹配那些在intent-filter中声明了<category android:name="android.intent.category.DEFAULT">这个category的Activity,
针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息,例如queryIntentServices
、queryBroadcastReceivers
等方法
有一类action和category比较重要,它们在一起用来标明这是一个入口Activity,并且会出现在系统的应用列表中。
<intent-filter> |
OK,本章结束,谢谢阅读。