官方文档称 以这种方式启动的Activity总是属于一个任务的根Activity。果真如此吗?本文将为你解开Activity的"singleTask"之谜。

任务(Task)是个什么样的概念

每一个Activity代表一个用户操作, 用户为了完成某个功能而执行的一系列操作就形成了一个Activity序列,这个序列在Android应用程序中就称之为任务,它是从用户体验的角度出发,把一组相关的Activity组织在一起而抽象出来的概念

具体配置请参考官方文档:

http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html

它是这样介绍以"singleTask"方式启动的Activity的:

The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.

它明确说明,以"singleTask"方式启动的Activity,全局只有唯一个实例存在,因此,当我们第一次启动这个Activity时,系统便会创建一个新的任务,并且初始化一个这样的Activity的实例,放在新任务的底部,如果下次再启动这个Activity时,系统发现已经存在这样的Activity实例,就会调用这个Activity实例的onNewIntent成员函数,从而把它激活起来。从这句话就可以推断出,以"singleTask"方式启动的Activity总是属于一个任务的根Activity。

但是文档接着举例子说明,当用户按下键盘上的Back键时,如果此时在前台中运行的任务堆栈顶端是一个"singleTask"的Activity,系统会回到当前任务的下一个Activity中去,而不是回到前一个Activity中去,如下图所示:

真是坑爹啊!有木有!前面刚说"singleTask"会在新的任务中运行,并且位于任务堆栈的底部,这里在Task B中,一个赤裸裸的带着"singleTask"标签的箭头无情地指向Task B堆栈顶端的Activity Y,刚转身就翻脸不认人了呢!

狮屎胜于熊便,我们来做一个实验吧,看看到底在启动这个"singleTask"的Activity的时候,它是位于新任务堆栈的底部呢,还是在已有任务的顶部。

具体分析过程请参考老罗的<< 解开Android应用程序组件Activity的"singleTask"之谜 >>

到这里,思路就理清了,虽然SubActivity的launchMode被设置为"singleTask"模式,但是它并不像官方文档描述的一样:The system creates a new task and instantiates the activity at the root of the new task,而是在跟它有相同taskAffinity的任务中启动,并且位于这个任务的堆栈顶端,于是,前面那个图中,就会出现一个带着"singleTask"标签的箭头指向一个任务堆栈顶端的Activity Y了。
        那么,我们有没有办法让一个"singleTask"的Activity在新的任务中启动呢?答案是肯定的。从上面的代码分析中,只要我们能够进入函数startActivityUncheckedLocked的这个if语句中:

  1. if (r.resultTo == null && !addingToTask
  2. && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
  3. // todo: should do better management of integers.
  4. mService.mCurTask++;
  5. if (mService.mCurTask <= 0) {
  6. mService.mCurTask = 1;
  7. }
  8. r.task = new TaskRecord(mService.mCurTask, r.info, intent,
  9. (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
  10. if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
  11. + " in new task " + r.task);
  12. newTask = true;
  13. if (mMainStack) {
  14. mService.addRecentTaskLocked(r.task);
  15. }
  16. }

那么,这个即将要启动的Activity就会在新的任务中启动了。进入这个if语句需要满足三个条件,r.resultTo为null,launchFlags的Intent.FLAG_ACTIVITY_NEW_TASK位为1,并且addingToTask值为false。从上面的分析中可以看到,当即将要启动的Activity的launchMode为"singleTask",并且调用startActivity时不要求返回要启动的Activity的执行结果时,前面两个条件可以满足,要满足第三个条件,只要当前系统不存在affinity属性值等于即将要启动的Activity的taskAffinity属性值的任务就可以了。

我们可以稍微修改一下上面的AndroidManifest.xml配置文件来做一下这个实验:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="shy.luo.task"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <application android:icon="@drawable/icon" android:label="@string/app_name">
  7. <activity android:name=".MainActivity"
  8. android:label="@string/app_name"
  9. android:taskAffinity="shy.luo.task.main.activity">
  10. <intent-filter>
  11. <action android:name="android.intent.action.MAIN" />
  12. <category android:name="android.intent.category.LAUNCHER" />
  13. </intent-filter>
  14. </activity>
  15. <activity android:name=".SubActivity"
  16. android:label="@string/sub_activity"
  17. android:launchMode="singleTask"
  18. android:taskAffinity="shy.luo.task.sub.activity">
  19. <intent-filter>
  20. <action android:name="shy.luo.task.subactivity"/>
  21. <category android:name="android.intent.category.DEFAULT"/>
  22. </intent-filter>
  23. </activity>
  24. </application>
  25. </manifest>

注意,这里我们设置MainActivity的taskAffinity属性值为"shy.luo.task.main.activity",设置SubActivity的taskAffinity属性值为"shy.luo.task.sub.activity"。重新编译一下程序,在模拟器上把这个应用程序再次跑起来,用“adb shell dumpsys activity”命令再来查看一下系统运行的的任务,就会看到:

  1. Running activities (most recent first):
  2. TaskRecord{4069c020 #4 A shy.luo.task.sub.activity}
  3. Run #2: HistoryRecord{40725040 shy.luo.task/.SubActivity}
  4. TaskRecord{40695220 #3 A shy.luo.task.main.activity}
  5. Run #1: HistoryRecord{406b26b8 shy.luo.task/.MainActivity}
  6. TaskRecord{40599c90 #2 A com.android.launcher}
  7. Run #0: HistoryRecord{40646628 com.android.launcher/com.android.launcher2.Launcher}

这里就可以看到,SubActivity和MainActivity就分别运行在不同的任务中了。

至此,我们总结一下,设置了"singleTask"启动模式的Activity的特点

1. 设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的任务存在;如果存在这样的任务,它就会在这个任务中启动,否则就会在新任务中启动。因此,如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。

2. 如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的堆栈顶端中。

Activity的"singleTask"之谜的更多相关文章

  1. 解开Android应用程序组件Activity的"singleTask"之谜

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6714543 在Android应用程序中,可以配 ...

  2. Activity以singleTask模式启动,intent传值的解决办法

    转载请注明出处,谢谢http://blog.csdn.net/harryweasley/article/details/46557827 因为项目中,有一个消息推送的功能,每次推送一个消息,就会开启F ...

  3. 我的Android进阶之旅------>Android Activity的singleTask加载模式和onActivityResult方法之间的冲突

    今天调试一个bug的时候,情景如下: 一个Activity A,需要用startActivityForResult方法开启Activity B.Activity B的launch mode被设置为si ...

  4. [Android]Activity跳转传递任意类型的数据、Activity为SingleTask时代替StartActivityForResult的解决方案

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4389674.html 需求:在ActivityA跳转到Acti ...

  5. Activity设置singleTask无法通过Intent获取值的问题

    AActivity跳转BActivity ,AActivity设置lauchmode = "SingleTask"的话,在getIntent无法获取BActivity里面的内容,无 ...

  6. 博客维护停止,需要的伙伴们移步http://blog.csdn.net/panhouye

    两个博客的维护着实费心,方便大家共同学习.督促.进步.感兴趣的伙伴们移步CSDN博客:http://blog.csdn.net/panhouye,博客目录如下: 1.Android中通过实现线程更新P ...

  7. Android常见面试笔试题目

    Android常见面试笔试题目 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 答:可以处理消息循环的线程,他是一个拥 ...

  8. Activity的Launch mode详解 singleTask正解

    Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance.以下逐一举例说明他们的区别: standard:Activity ...

  9. Activity的Launch mode详解,A B C D的singleTask模式

    本文参考了此文http://hi.baidu.com/amauri3389/blog/item/a54475c2a4b2f040b219a86a.html 另附 android task与back s ...

随机推荐

  1. N对括号的合法组合

    递归实现,需要注意以下几点: 1. 递归终止条件 2. 递归递推关系式 这里实际上是一个排列问题,只是排列需要满足条件在每一次递归调用时左括号数不能少于右括号数. 还有一点需要特别注意,当推出递归调用 ...

  2. 简单总结一下NotificationCenter、KVC、KVO、Delegate

    将最近总结的最常用的几种设计模式优势与区别自己小结一下,分享给大家. kvo只能用来对属性作出反应,而不会用来对方法或者动作作出反应,是实现一个对象与另外一个对象保持同步的一种方法,能够提供观察的属性 ...

  3. 20169210《Linux内核原理与分析》第六周作业

    这次博客分为两部分,第一部分是实验,第二部分是教材. 第一部分:实验 这周实验的内容是使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用,教学案例给的是time()获取系统时间的函数,我 ...

  4. 如何利用 _ViewStart.cshtml对页面添加代码?

    _ViewStart.cshtml 添加的代码会出现在页面的最上面(<html> 之前) .这样就造成了我原先很多页面出现兼容性问题(经难是因为<html>之前出现了其它的代码 ...

  5. spring入门之helloworld

    1.第一个spring例子 1.建立工程 建立一个java project就可以. 导包,helloworld包括下面两个最少的包: 到http://www.springsource.org/down ...

  6. 实现FTP断点续传

    应用需求: 网盘开发工作逐步进入各部分的整合阶段,当用户在客户端修改或新增加一个文件时,该文件要同步上传到服务器端对应的用户目录下,因此针对数据传输(即:上传.下载)这一块现在既定了三种传输方式,即: ...

  7. ASP.NET Web API(二):安全验证之使用HTTP基本认证

    在前一篇文章ASP.NET Web API(一):使用初探,GET和POST数据中,我们初步接触了微软的REST API: Web API. 我们在接触了Web API的后就立马发现了有安全验证的需求 ...

  8. Retrofit2源码分析(一)

    本文将顺着构建请求对象→构建请求接口→发起同步/异步请求的流程,分析retrofit2是如何实现的. 组成部分 Retrofit2源码主要分为以下几个部分: retrofit retrofit-ada ...

  9. 【Oracle】INSERT INTO SELECT语句和SELECT INTO FROM语句的区别

    >>>>>>>>>>>>>>>>>>>>>>>>> ...

  10. [DEncrypt] Encrypt--加密/解密/MD5加密 (转载)

    点击下载  Encrypt.zip 这个类是关于加密,解密的操作,文件的一些高级操作1.Encrypt加密2.Encrypt解密3.Encrypt MD5加密看下面代码吧 /// <summar ...