Android 启动模式--任务(Task)--桟 的误区
Android 启动模式--任务(Task)--桟 的误区
写这篇文章是因为前几天的一次面试,面试官说
SingleInstance
模式会新建一个桟,而SingleTask
不会.首先不说这个对不对(非要说对错的话,那就是错.),因为这句话是含糊不清的.桟?只的是返回桟? 还是任务桟?有没有考虑taskAffinity
属性?所以笼统的那样说是不对的.这篇文章一是为了记录,二是为了说清楚----任务(Task)& 桟(返回桟,任务桟).
概念
桟(堆栈:stack)
栈的基本特点:
- 先入后出,后入先出。
- 除头尾节点之外,每个元素有一个前驱,一个后继。
任务
任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。
返回桟
在官方文档中,找不到关于返回桟的概念,但是按照官方文档在描述时的语境,可以理解为响应Android返回键的顺序队列.这是站在用户的直观感受上的描述.Android 内部的描述单位只有任务(Task).
任务桟
在官方文档中没有这个概念.我为了方便理解,把任务
中按照桟
规则的顺序队列的 一系列 Activity 称为 任务栈
.
个人理解
由于 官方文档中没有详细的说明什么是返回桟
,在实际的开发中如果以栈为基本度量单位
的话,很容易被自己绕晕,如果按照任务
为基本度量单位的话就很容易理清楚 Android 四大启动模式
了.我个人把返回栈理解为这样子的: 返回桟仅有一个(因为返回键只有一个),返回桟操作的基本元素是任务
,返回键操作的是返回桟中处于前台的任务
,每个任务
维护着一系列 Activity 组成的栈
以响应返回键.
Activity 四大启动模式
- "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 应始终在其自己的任务中打开(通过在 元素中指定 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 继承为应用设置的亲和关系(请参阅 元素的 taskAffinity 属性)。 应用默认亲和关系的名称是 元素设置的软件包名称。
测试 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
达到创建新任务
的目的,创建的任务
可添加其他元素.
最后,说的不对的欢迎交流.
Android 启动模式--任务(Task)--桟 的误区的更多相关文章
- android 启动模式介绍
Android启动模式 (1)Task:与Android系统是个多任务的系统中的任务是不同的.后者更倾向于多进程和多线程来说的,而这里的任务与application(应用程序)和activity(活动 ...
- android启动模式2
Android中的启动模式(下) 在这篇文章中,我会继续跟大家分享有关于Android中启动模式的相关知识.当然,如果对这个启动模式还不完全了解或者没有听过的话,可以先看看我之前写的有关于这个知识点的 ...
- 我所理解的Android 启动模式
首先,这是从 一个开源网站转载的,觉得写得不错,对我们之前理解的activity的启动模式是一个新的理解方式,并给出实际的应用场景. 任务栈是什么 任务栈Task,是一种用来放置Activity实例的 ...
- android启动模式对于体验的影响
说到Android的启动模式.懂Android的人肯定都懂. 通过设置启动模式我们不仅能够节省内存的使用.还能达到更好的体验,比方我们打开一个应用,点击home键回到主界面的时候程序是没有被kill掉 ...
- Android启动模式
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...
- Android启动模式launchMode
在Android里,有4种Activity的启动模式并分别介绍下: standard singleTop singleTask singleInstance AndroidManifest.xml配置 ...
- Android启动模式(三种)
1,标准启动模式 通过任务栈,每点一次button,将每一个实例都压入,然后点返回键时候,就弹出之前压入的实例. 每一次的地址都是不同的 测试代码:通过创建一个button和textView来显示本身 ...
- Android启动模式之singleinstance的坑
前言 在实际应用中,使用singleinstance启动模式时,会遇到一些奇奇怪怪的问题.Android有四种启动模式,分别是standard,singleTop,singleTask,singleI ...
- Android -- 启动模式
Android的启动模式分为四种: standard 模式启动模式,每次激活Activity时都会创建Activity,并放入任务栈中. singleTop 如果在任务的栈顶正好存在该Activity ...
随机推荐
- Spring @Aspect进行类的接口扩展
Spring @Aspect进行类的接口扩展: XML: <?xml version="1.0" encoding="UTF-8"?> <be ...
- Flex移动应用程序开发的技巧和窍门(四)
范例文件 flex-mobile-dev-tips-tricks-pt4.zip 这是本系列文章的第四部分,该系列文章涵盖Flex移动开发的秘诀与窍门. 第一部分关注切换视图以及切换执行应用时的数据处 ...
- JavaScript原生的节点操作
前言:原生是Javascript的基础,还是需要多多重视,时间长都忘记了,现在整理一下. 获取子节点 children 不是标准的dom属性,但是几乎被所有浏览器支持.不包含文本节点. 注意:在IE中 ...
- 安装python2.7.13-64bit & Pycharm在两个python版本之间切换
本来已经安装了32位的python27,但在使用轮廓系数评估k-means模型的优良性时,出现了内存溢出的报错.原来32为的python编译器最多只能使用4GB的内存,所以就打算换成64位的pytho ...
- CodeForces 725A
A. Jumping Ball time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...
- Hadoop权威指南: 专有数据类型
Writable 和 WritableComparable接口 Writable接口 ** Writable接口的主要目的是,当数据在网络上传输或从硬盘读写时,提供数据的序列化和反序列化机智 ** * ...
- 通过判断cookie过期方式向Memcached中添加,取出数据(Java)
应用场景:在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够 ...
- HTML5 & CSS3 初学者指南(4) – Canvas使用
介绍 传统的HTML主要用于文本的创建,可以通过<img>标签插入图像,动画的实现则需要第三方插件.在这方面,传统的HTML极其缺乏满足现代网页多媒体需求的能力.HTML5的到来,带来了新 ...
- webAppbuilder微件使用教程3 地理处理微件
webAppbuilder微件使用教程 --微件使用进阶地理处理微件 By 李远祥 地理处理是GIS解决问题的关键部分,也是其灵魂所在.由于WebAppBuilder框架的限制,用户如果想要非常灵活的 ...
- 【LeetCode题解】链表Linked List
1. 链表 数组是一种顺序表,index与value之间是一种顺序映射,以\(O(1)\)的复杂度访问数据元素.但是,若要在表的中间部分插入(或删除)某一个元素时,需要将后续的数据元素进行移动,复杂度 ...