android Activity启动模式LaunchMode

首先说一下为何activity需要启动模式。

记得有一次一位同事问过我一个问题,他从项目首页A跳转选地址页面B,选好地址回到首页面A,一切正常,双击back退出程序,发现还在首页面A。
由于我处理这种问题的时候,一般是B页面直接调用finish方法,结果解决问题绕了很大的弯路,最后发现他是在B页面调用startactivity方法回到A,这也是最根本问题所在。

在默认的情况下,当我们多次启动一个activity的时候,系统就会创建多个实例并把他们一一放入任务栈中,当我们按back键或者调用finish方法的时候,会发现这些activity一一回退。任务栈是一种“后进先出”的栈结构,这比较好理解,每次按一下back键或调用finish方法,就会有一个activity出栈,直到栈空为止,当栈中无任务activity的时候,系统就会回收这个任务栈。
这就有问题了,多次启动同一个activity,系统就会重复创建多个实例,能不能就创建一次呢?这就涉及到了android的启动模式(LaunchMode)

目前activity有四种启动模式:standard(标准模式)、singleTop(栈顶复用模式)、singleTask(栈内复用模式)、singleInstance(单实例模式)。

  1. standard:标准模式,也是系统默认的模式。每次启动一个activity,都会重新创建一个新的实例,不管这个实例是否已经存在。在这种模式下,谁启动了这个activity,这个activity就运行在启动他的哪个activity所在的栈中。
  1. singleTop:栈顶复用模式,在这种模式下,如果新activity已经位于任务栈的栈顶,那么此activity不会被重新创建。也不涉及生命周期的变化,同时他的onNewIntent方法会被调用,通过此方法的参数我们可以取出当前的请求信息。但如果新的activity实例已经存在但并不在栈顶,那么activity仍然会重建。
  1. singleTask:栈内复用模式,这是一种单实例模式,在这种模式下,activity只要在栈中存在(参照属性TaskAffinity),那么多次启动此activity都不会重新创建实例,系统也会回调其onNewIntent。当跳转该activity的时候,默认具有clearTop效果,该activity栈顶所有activity都会销毁。如果不存在,那么根据TaskAffinity查找该任务栈,如果任务栈不存在,先创建任务栈再将activity放入栈中。
  1. singleInstance:单实例模式。这是一种加强的singleTask模式,他和singleTask的区别就是,此模式的activity只能单独的位于一个任务栈。
android Activity启动模式LaunchMode_第1张图片

【说明:此问题我有所怀疑,所以进行测试,如上图三个activity,测试流程activity1 -> activity2 -> activity3 -> activity4 -> back键,发现按完back键之后,回到acvitity1,此测试说明从singleInstance再次启动的activity,放入了默认任务栈】
那么,标准模式的TaskAffinity是否生效呢?继续测试:

android Activity启动模式LaunchMode_第2张图片

【说明:同上面一样的测试,测试流程activity1 -> activity2 -> activity3 -> activity4 -> back键,发现按完back键之后,回到acvitity2,此测试说明从singleInstance再次启动的activity,放在哪个任务栈,是由TaskAffinity决定的】

那么什么是TaskAffinity呢?

TaskAffinity,也就是activity所需的任务栈,翻译为任务相关性。这个参数标识了一个activity所需要的任务栈的名字,默认情况下,所有activity所需的任务栈的名字为应用包名。我们可以为每个activity单独指定TaskAffinity属性,也如上面途中所示。

说到TaskAffinity,不得不提一下allowTaskReparenting属性

allowTaskReparenting一般和TaskAffinity配对使用,当一个应用A启动了应用B的某个Activity后,如果这个activity的allowTaskReparenting属性为true,那么当应用B被启动后,此activity会直接从应用A的任务栈转移到应用B的任务栈中。

提示:参照standard介绍,如不设置该属性,activity应该在A栈中,所以默认情况下如果应用B栈中本就有任务,这时候按back键应该还在B应用中而非A应用中。

上面图片所示的为AndroidMenifest中的启动方法,还有一种Intent中设置标志位来为activity指定启动模式

Intent intent = new Intent(this, GuideDetailsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

这两种方式都可以为activity指定启动模式,区别1:优先级上,第二种方式的优先级要高于图片中所示,当两种同事存在时,以第二种方式为准。区别2:上述两种方式在限定范围上有所不同,比如第一种方式无法直接为activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为activity指定singleInstance

Activity的Flags

activity的Flags有很多,这里列举一些比较常见的标记位。大部分情况下,我们不需要为activity指定标记位,这里只简单介绍下。
FLAG_ACTIVITY_NEW_TASK 这个标记位的作用是指定singleTask启动模式
FLAG_ACTIVITY_SINGLE_TOP 这个标记位的作用是指定singleTop启动模式
FLAG_ACTIVITY_CLEAR_TOP 具有此标记的activity,当他启动时,在同一个任务栈中所有位于他上面的activity都要出栈。这个标记一般和singleTask启动模式一起出现,在这种模式下,如果实例已经存在,那么系统就会调用他的onNewIntent。如果被启动的activity采用standard模式启动,那么他连同他之上的activity都要出栈,系统会创建新的activity实例并放入栈顶。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 相当于activity的android:excludeFromRecents="true"属性,表示该activity不会出现在历史activity中,对于这个我进行了一个测试:

android Activity启动模式LaunchMode_第3张图片

模拟器测试该属性不生效,本人真机测试用的是乐视手机乐1s,只有按图中,在初始activity中设置才生效,其他activity中无效果。如果有其他情况,欢迎评论中留言,感激不尽!

Activity的IntentFilter匹配规则

启动activity分为两种方法,显式调用和隐式调用,显示调用就是startactivity方式,应该都比较熟悉,隐式调用就是IntentFliter形式,IntentFliter为过滤属性,限制匹配到合适的activity,二者共存的时候,以显式为主。

IntentFliter中的过滤信息有action(行动)、category(类别)、data(数据),下面是一个过滤规则的示例:

android Activity启动模式LaunchMode_第4张图片

只有一个intent同时匹配action类别,category类别,data类别才算完全匹配,只有完全匹配才能成功启动目标activity。

下面详细分析各种属性的匹配规则:

  1. action的匹配规则:action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action,action的匹配规则是intent中的action必须能够和过滤规则中的action字符串完全一样,多个action只要能过滤规则中的任何一个action相同即可匹配成功,intent中如果没有指定action,择匹配失败。
  1. category匹配规则:category是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category,要求intent可以没有category,但是如果你一旦有category,不管有几个,每个都要能够和过滤规则中的任何一个category相同。(如果没设置,默认android.intent.category.DEFAULT)
  1. data的匹配规则:data和action类似,如果规则中定义了data,那么intent中必须也要定义可匹配的data。
    data的语法定义:
    android:scheme="string"
    android:host="string"
    android:port="string"
    android:path="string"
    android:pathPattern="string"
    android:pathPrefix="string">

    data由两部分组成,mimeType和URI,mimeType指媒体类型,比如:image/jpeg、video/等,可以标识图片、文本、视频等不同的媒体格式(此时如不指定,默认过滤规则为content和file),而URI包含的数据就比较多了,下面是URI的结构:
    ://:/[||]
    比如:
    content://come.exmple.project:200/folder/subfolder/etc
    http://www.baidu.com:80/search/info
    下面介绍每个数据的含义:
    Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效,这也意味着URI是无效的。
    Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,这也意味着URI是无效的。
    Port:URI的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的。
    Path、pathPattern和pathPrefix:这三个参数表述路径信息,其中path表示完整的路径信息;pathPattern也标识完整的路径信息,但是他里面可以包含通配符“
    ”,“”标识0个活多个字符,需要注意的是,由于正则表达式的规范,如果想标识真实的字符串,那么“”要写成“\*”,""要写成“\\”;pathPrefix表示路径的前缀信息。
0data匹配规则:要求intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data,这里的完全匹配是指过滤规则中出现的data部分也出现在了intent中的data。

当我们通过隐式方式启动一个activity的时候,可以做一下判断,看是否有activity能够匹配我们的隐式intent

判断方式有两种

  1. 采用PackageManager的resolveActivity方法或者Intent 的resolveActivity方法,如果他们找不到匹配的activity就会返回null。
  2. PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity方法不同的是,他不是返回最佳匹配的activity信息而是返回所有成功匹配的activity信息。

相关文章:android Activity生命周期

本文为本人学习笔,如有任何问题,欢迎评论中指出,感激不尽!
二维码.jpg

推荐下本人的微信公众号,本博客及其他方面的消息会定期同步到公众号上面!

你可能感兴趣的:(android Activity启动模式LaunchMode)