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 应始终在其自己的任务中打开(通过在 <activity> 元素中指定 singleTask 启动模式)。这意味着,如果您的应用发出打开 Android 浏览器的 Intent,则其 Activity 与您的应用位于不同的任务中。相反,系统会为浏览器启动新任务,或者如果浏览器已有任务正在后台运行,则会将该任务上移一层以处理新 Intent。

diagram_backstack_singletask_multiactivity.png

无论 Activity 是在新任务中启动,还是在与启动 Activity 相同的任务中启动,用户按“返回”按钮始终会转到前一个 Activity。 但是,如果启动指定 singleTask 启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的所有 Activity。 上图显示了这种情况。

上图 显示如何将启动模式为“singleTask”的 Activity 添加到返回栈。 如果 Activity 已经是某个拥有自己的返回栈的后台任务的一部分,则整个返回栈也会上移到当前任务的顶部。

参考:2017-03-14日摘自官方文档(后期可能有变更以官网为准)

关于android:taskAffinity属性

android:taskAffinity

与 Activity 有着亲和关系的任务。从概念上讲,具有相同亲和关系的 Activity 归属同一任务(从用户的角度来看,则是归属同一“应用”)。 任务的亲和关系由其根 Activity 的亲和关系确定。
亲和关系确定两件事 - Activity 更改到的父项任务(请参阅 allowTaskReparenting 属性)和通过 FLAG_ACTIVITY_NEW_TASK 标志启动 Activity 时将用来容纳它的任务。
默认情况下,应用中的所有 Activity 都具有相同的亲和关系。您可以设置该属性来以不同方式组合它们,甚至可以将在不同应用中定义的 Activity 置于同一任务内。 要指定 Activity 与任何任务均无亲和关系,请将其设置为空字符串。
如果未设置该属性,则 Activity 继承为应用设置的亲和关系(请参阅 <application> 元素的 taskAffinity 属性)。 应用默认亲和关系的名称是 <manifest> 元素设置的软件包名称。

参考:2017-03-14日摘自官方文档(后期可能有变更以官网为准)

测试 SingleTaskSingleInstance的异同

我写了一个Demo测试了下:

当我把SingleTaskA配置为:

<activity android:name=".SingleTaskA" android:launchMode="singleTask" android:taskAffinity="com.didikee.temp"/>

SingleTaskA中启动一个标准模式的StandardBActivity:

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 还是继续添加在之前任务中.

总结:

  1. Android 是以任务为核心的,不要被栈带偏了.
  2. SingleInstance 一定会新建一个任务,并且该任务中仅包含一个实例.
  3. SingleTask默认不会创建新任务,但是可以通过taskAffinity达到创建新任务的目的,创建的任务可添加其他元素.

最后,说的不对的欢迎交流.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容