写这篇文章是由于前几天的一次面试,面试官说
SingleInstance
模式会新建一个桟,而SingleTask
不会.首先不说这个对不对(非要说对错的话,那就是错.),由于这句话是含糊不清的.桟?只的是返回桟? 仍是任务桟?有没有考虑taskAffinity
属性?因此笼统的那样说是不对的.这篇文章一是为了记录,二是为了说清楚----任务(Task)& 桟(返回桟,任务桟).html
桟(堆栈:stack)android
栈的基本特色:面试
先入后出,后入先出。浏览器
除头尾节点以外,每一个元素有一个前驱,一个后继。网络
任务app
任务是指在执行特定做业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。ide
返回桟
在官方文档中,找不到关于返回桟的概念,可是按照官方文档在描述时的语境,能够理解为响应Android返回键的顺序队列.这是站在用户的直观感觉上的描述.Android 内部的描述单位只有任务(Task).测试
任务桟
在官方文档中没有这个概念.我为了方便理解,把任务
中按照桟
规则的顺序队列的 一系列 Activity 称为 任务栈
.ui
因为 官方文档中没有详细的说明什么是返回桟
,在实际的开发中若是以栈为基本度量单位
的话,很容易被本身绕晕,若是按照任务
为基本度量单位的话就很容易理清楚 Android 四大启动模式
了.我我的把返回栈理解为这样子的: 返回桟仅有一个(由于返回键只有一个),返回桟操做的基本元素是任务
,返回键操做的是返回桟中处于前台的任务
,每一个任务
维护着一系列 Activity 组成的栈
以响应返回键.google
"standard"(默认模式)
默认。系统在启动 Activity 的任务中建立 Activity 的新实例并向其传送 Intent。Activity 能够屡次实例化,而每一个实例都可属于不一样的任务,而且一个任务能够拥有多个实例。
"singleTop"
若是当前任务的顶部已存在 Activity 的一个实例,则系统会经过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是建立 Activity 的新实例。Activity 能够屡次实例化,而每一个实例都可属于不一样的任务,而且一个任务能够拥有多个实例(但前提是位于返回栈顶部的 Activity 并非 Activity 的现有实例)。
例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。若是 D 具备默认的 "standard" 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。可是,若是 D 的启动模式是"singleTop",则 D 的现有实例会经过 onNewIntent() 接收 Intent,由于它位于堆栈的顶部;而堆栈仍为 A-B-C-D。可是,若是收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即使其启动模式为 "singleTop" 也是如此。
注:为某个 Activity 建立新实例时,用户能够按“返回”按钮返回到前一个 Activity。 可是,当 Activity 的现有实例处理新 Intent 时,则在新 Intent 到达 onNewIntent() 以前,用户没法按“返回”按钮返回到 Activity 的状态。
"singleTask"
系统建立新任务并实例化位于新任务底部的 Activity。可是,若是该 Activity 的一个实例已存在于一个单独的任务中,则系统会经过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是建立新实例。一次只能存在 Activity 的一个实例。
注:尽管 Activity 在新任务中启动,可是用户按“返回”按钮仍会返回到前一个 Activity。
"singleInstance".
与 "singleTask" 相同,只是系统不会将任何其余 Activity 启动到包含实例的任务中。该 Activity 始终是其任务惟一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。
咱们再来看另外一示例,Android 浏览器应用声明网络浏览器 Activity 应始终在其本身的任务中打开(经过在 <activity> 元素中指定 singleTask 启动模式)。这意味着,若是您的应用发出打开 Android 浏览器的 Intent,则其 Activity 与您的应用位于不一样的任务中。相反,系统会为浏览器启动新任务,或者若是浏览器已有任务正在后台运行,则会将该任务上移一层以处理新 Intent。
不管 Activity 是在新任务中启动,仍是在与启动 Activity 相同的任务中启动,用户按“返回”按钮始终会转到前一个 Activity。 可是,若是启动指定 singleTask 启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的全部 Activity。 上图显示了这种状况。
上图 显示如何将启动模式为“singleTask”的 Activity 添加到返回栈。 若是 Activity 已是某个拥有本身的返回栈的后台任务的一部分,则整个返回栈也会上移到当前任务的顶部。
android:taskAffinity
属性android:taskAffinity
与 Activity 有着亲和关系的任务。从概念上讲,具备相同亲和关系的 Activity 归属同一任务(从用户的角度来看,则是归属同一“应用”)。 任务的亲和关系由其根 Activity 的亲和关系肯定。
亲和关系肯定两件事 - Activity 更改到的父项任务(请参阅 allowTaskReparenting 属性)和经过 FLAG_ACTIVITY_NEW_TASK 标志启动 Activity 时将用来容纳它的任务。
默认状况下,应用中的全部 Activity 都具备相同的亲和关系。您能够设置该属性来以不一样方式组合它们,甚至能够将在不一样应用中定义的 Activity 置于同一任务内。 要指定 Activity 与任何任务均无亲和关系,请将其设置为空字符串。
若是未设置该属性,则 Activity 继承为应用设置的亲和关系(请参阅 <application> 元素的 taskAffinity 属性)。 应用默认亲和关系的名称是 <manifest> 元素设置的软件包名称。
SingleTask
与SingleInstance
的异同我写了一个Demo测试了下:
当我把SingleTaskA
配置为:
<activity android:name=".SingleTaskA" android:launchMode="singleTask" android:taskAffinity="com.didikee.temp"/>
在SingleTaskA
中启动一个标准模式的StandardB
Activity:
public class SingleTaskA extends BaseLauncherModeActivity { @Override protected Class gotoActivity() { return StandardB.class; } @Override protected String setModeTextShow() { return Constant.SINGLE_TASK; } }
而在StandardB
中,我一直启动StandardB
本身:
public class StandardB extends BaseLauncherModeActivity { @Override protected Class gotoActivity() { return StandardB.class; } @Override protected String setModeTextShow() { return Constant.STANDARD+"Repeat"; } }
补充下基类:
public abstract class BaseLauncherModeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_launcher_mode); String text = setModeTextShow(); Button button = (Button) findViewById(R.id.bt); button.setText("模式: "+text); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Class aClass = gotoActivity(); if (aClass==null)return; startActivity(new Intent(getApplicationContext(),aClass)); } }); int taskId = getTaskId(); Log.d(Constant.TAG,"taskId: "+taskId); } protected abstract Class gotoActivity(); protected abstract String setModeTextShow(); }
获得以下结果:
03-14 10:32:32.287 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 26 03-14 10:32:37.691 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 26 03-14 10:32:42.120 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:45.961 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:52.105 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:53.531 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:55.748 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:56.763 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27
能够看到,SingleTaskA
启动了一个新的任务(Task)
,id为27,以后在SingleTaskA中启动StandardB
并无作其余的操做,而是直接在刚刚SingleTaskA
启动的任务
中添加.
如今我去掉 taskAffinity
属性的定义,获得:
03-14 16:42:10.604 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:15.261 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:16.865 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:19.197 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:20.232 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:21.049 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:22.180 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29
可见,默认是不会建立新的任务
的.
如今,把SingleTaskA 改成 SingleInstanceA,而后在其中能够启动StandardB
(上面有代码),获得以下结果:
03-14 16:46:59.669 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:09.367 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:10.602 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 31 03-14 16:47:12.013 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:14.438 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:15.429 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30
可见,SingleInstanceA
新建了一个任务
id为31,在 SingleInstanceA 中启动 StandardB 仍是继续添加在以前任务
中.
Android 是以任务
为核心的,不要被栈带偏了.
SingleInstance
必定会新建一个任务
,而且该任务
中仅包含一个实例.
SingleTask
默认不会建立新任务
,可是能够经过taskAffinity
达到建立新任务
的目的,建立的任务
可添加其余元素.
最后,说的不对的欢迎交流.