转转请注明出处:http://blog.csdn.net/u011510784/article/details/50856323
Activity LaunchMode,TaskAffinity及Flag相关资料详细整理.
一.Activity的LaunchMode
(1) standard:标准模式,也是系统默认模式,每次启动一个Activity都会重建一个新的实例,不管这个Activity是否已经存在.这种模式下,一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中.但是如果是非Activity的context启动它时,会报错,需要为待启动的Activity添加FLAG_ACTIVITY_NEW_TASK标记.这时,待启动的Activity实际上是以singleTask模式启动的.
(2)singleTop:栈顶复用模式.这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息.这个Activity的onCreate,onStart不会被系统调用.如果新Activity的实例已存在但是不是位于栈顶,那么这个新Activity仍会被重新创建.
(3)singleTask:栈内复用模式.在一个任务栈中,只要目标Activity的实例存在,那么多次启动此Activity都不会重新创建实例,系统会调用存在的那个Activity实例的onNewIntent方法.同时singleTask模式默认具有clearTop的效果,如果当前任务栈的情况为ABCD(表示Activity实例),用这个模式启动B,那么会导致栈内所有在B上面的Activity全部出栈,于是此任务栈中的情况变为AB.
(4)singleInstance:单实例模式.具有此模式的Activity只能单独的位于一个任务栈中,如果 Activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续的请求都不会创建新的实例,除非这个独特的栈被系统销毁了.
有两种方法可以指定Activity的启动模式,第一种是通过AndroidMenifest指定,另一种是通过Intent中设置标志来指定.区别是第二种的优先级高于第一种,其次是这两种方式的限定范围不同,比如第一种无法为Activity指定FLAG_ACTIVITY_CLEAR_TOP标志,二第二种方法无法为Activity指定singleInstance模式.
二.Activity的TaskAffinity
TaskAffinity可以翻译为任务相关性,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名,我们可以为每个Activity单独指定TaskAffinity属性,这个属性值不能和包名相同,否则相当于没有指定.TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,其他情况下没有意义.
1.当TaskAffinity和singleTask启动模式配对使用时,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中.
2.当TaskAffinity和allowTaskReparenting结合使用时.举个例子,当应用A启动应用B的某个Activity后,如果这个 Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中.比如应用A启动了应用B的一个Activity C,然后按home键回到桌面,然后在单击应用B的桌面图标,这时启动的不是应用B的主Activity,而是Activity C,或者说C从应用A的任务栈转移到了应用B的任务栈.可以这么理解由于应用A启动了Activity C,这时C只能运行在A的任务栈中,但是C属于应用B,并且C的TaskAffinity值为应用B的包名,所以当B启动后,B会创建自己的任务栈,这时系统发现C所需要的任务栈被创建了,所以把C从A的任务栈转移到了B的任务栈.
现在看一个例子:假设在一个应用中有三个Activity,分别是MainActivity A, SecondActivity B, 和ThirdActivity C .我们将B和C设置成singleTask并指定它们的TaskAffinity属性为"com.test.mytask",这个属性值为字符串,并且中间必须含有包名分隔符"." ,然后打开应用,进入A,在A中启动B,在B中启动C,在C中启动A,最后在A中启动B,现在按2次back键,会看到哪个Activity呢?答案是回到了桌面,下面分析一下:
A没有指定任何模式和属性,所以它是standard模式,A的TaskAffinity属性值是默认值即应用的包名,我们设置了B和C为singleTask模式,并且具有相同的TaskAffinity值"com.test.mytask".当A启动B时,会为B创建一个新的名为"com.test.mytask"的栈,B启动C时,由于C所需要的任务栈已经创建了,所以C的实例会直接进入B所在的那个栈,接着C在启动A,A是standard模式,所以A会创建一个新的实例并添加到启动它的那个Activity所在的栈中,即名为"com.test.mytask"的这个栈.这时存在两个A的实例,一个在名为应用包名的任务栈中,另一个在名为"com.test.mytask"的任务栈中,现在名字为包名的任务栈里面只有一个A,我们指定的"com.test.mytask"这个栈,里面的Activity为BCA,接下来A再启动B,由于B是singleTask模式,B需要回到栈顶,那么它上面的Activity都会出栈,这时这个栈中只存在一个Activity B,然后按back键,B出栈,B所在的任务栈不存在,这时回到后台任务栈,并把A显示出来,接着再按back,回到桌面.
三.IntentFilter的匹配规则.
启动Activity分为两种方式,显示调用和隐式调用.显示调用需要明确的指定被启动对象的组件信息,包括包名和类名.隐式调用需要intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity. IntentFilter中的过滤信息有action,category,data,需要同时匹配过滤列表中的action,category,data信息,否则匹配失败.一个过滤列表中的action,category和data可以有多个,所有的action,category,data分别构成不同类别,同一类别中的信息共同约束当前类别的匹配过程.只有一个intent同时匹配action类别,category类别,data类别才算完全匹配,否则启动不了目标Activity.另外,一个Activity中可以有多个intent-filter,一个intent只要能匹配任何一组intent-filter即可成功启动对应的Activity.如:
<span style="font-family:Comic Sans MS;font-size:14px;"><span style="font-family:Comic Sans MS;font-size:14px;"> <activity android:name="com.jiaxu.TestActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity></span></span>
下面分析各个属性的匹配规则.
1.action的匹配规则:
action是一个字符串,系统定义了一些action,我们也可以定义自己的action. action的匹配规则是intent中任何一个action的值能够和过滤规则中任何一个action的值完全相同,那么匹配成功.但是如果过滤规则中指定了action,intent中没有设置action那么匹配失败,另外action是区分大小写的.
2.category的匹配规则:
category是一个字符串,系统定义了一些category,我们也可以定义自己的category. category的匹配规则是intent中所有的category都必须和过滤规则中的其中某个category相同,但是intent中可以不指定category,因为系统在调用startActivity或者startActivityForResult的时候会默认为intent加上"android.intent.category.DEFAULT"这个category,所以这就要求我们必须在intent_filter中指定"android.intent.category.DEFAULT"这个category.
3.data的匹配规则和action类似,如果过滤规则中定义了data,那么intent中必须也要定义可匹配的data,只要匹配规律规则中的任意一个即可.
data的属性结构如下:
<data android:host="String"
android:mimeType="String"
android:path="String"
android:pathPattern="String"
android:pathPrefix="String"
android:port="String"
android:scheme="String"/>
data由两部分组成,mimeType和URI. mimeType指媒体类型,比如image/jpeg,audio/mpeg4-generic,video/* 等等,可以表示图片,文本,视频等不同的媒体格式,URI中数据较多,结构如下:
<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等,如果URI中没有指定scheme,那么整个URI无效.
Host:URI的主机名,比如www.baidu.com,如果URI没有指定host,那么整个URI无效.
Port:URI中的端口号,比如80.
Path,pathPattern和pathPrefix:这三个参数表示路径信息,其中path表示完整的路径信息:pathPattern也可以表示完整的路径信息,但是它里面可以包含通配符"*","*"表示0个或者多个任意字符,需要注意,由于正则表达式的规范,如果想表示真实的字符串,那么"*"要写成"\\*",\"要写成"\\\\",pathPrefix表示路径的前缀信息.如下一个例子:
<intent-filter>
<data android:mimeType="image/*"/>
......
</intent-filter>
这个规则指定了媒体类型为所有类型的图片,intent中data的mimeType属性为"image/*"才可以匹配, 过滤规则中没有指定URI,但是却有默认值URI的默认值为content和file,也就是说,虽然没有指定URI,但是Intent中data的URI中的的scheme部分必须写成content或者file才可以匹配,否则启动不了这个Activity:
intent.setDataAndType(Uri.parse("file://abc"),"image/png").
intent指定data时,需要调用setDataAndType方法,不能分开调用setData,setType这两个方法,因为这两个方法会互相清空对方的值.
当我们通过隐式方式启动一个Activity时,需要做一下判断,看看是否有Activity可以匹配我们指定的intent,判断方法可以使用PackageManageer的resolveActivity方法,queryIntentActivities方法,或者Intent的resolveActivity方法.
需要注意:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
int flags);
在这个方法中,第二个参数,我们要使用MATCH_DEFAULT_ONLY这个标记,它的含义是只匹配那些在intent-filter中声明了<category android:name="android.intent.category.DEFAULT"/>这个category的Activity.这样可以把intent-filter中category不含DEFAULT的那些Activity过滤掉,避免启动失败.在action和category中,有一个是我们常见也是很重的:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
两者共同标明这是一个入口Activity,并且会出现在系统的应用列表中.
四 Intet的FLAG.
FLAG属性较多,详情见:http://blog.csdn.net/u011510784/article/details/51003732