Activity的"singleTask"之谜

官方文档称 以这种方式启动的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语句中:

[java] view plaincopy

  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配置文件来做一下这个实验:

[java] view plaincopy

  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”命令再来查看一下系统运行的的任务,就会看到:

[html] view plaincopy

  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实例会位于任务的堆栈顶端中。

时间: 2024-07-30 03:18:36

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

解开Android应用程序组件Activity的&quot;singleTask&quot;之谜

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6714543 在Android应用程序中,可以配置Activity以四种方式来启动,其中最令人迷惑的就是"singleTask"这种方式了,官方文档称以 这种方式启动的Activity总是属于一个任务的根Activity.果真如此吗?本文将为你解开Activity的"singleTask"之谜. 在解开这个谜之前,我

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

转载请注明出处,谢谢http://blog.csdn.net/harryweasley/article/details/46557827 因为项目中,有一个消息推送的功能,每次推送一个消息,就会开启FunctionActivity,那么为了避免重复开启它,在退后的时候,多次出现该Activity,就将该Activity的启动模式变为singleTask. 这样在之后的多次启动该Activity,便会调用onNewIntent(Intent intent)方法. activity通过intent传

我的Android进阶之旅------&gt;Android Activity的singleTask加载模式和onActivityResult方法之间的冲突

今天调试一个bug的时候,情景如下: 一个Activity A,需要用startActivityForResult方法开启Activity B.Activity B的launch mode被设置为singleTask,那么在Activity B开启之后的瞬间(未等B返回任何result),Activity A中的onActivityResult方法就会被调用,并且收到一个RESULT_CANCEL的request code. 然后在ActivityB中做了一些逻辑之后,在Activity B通过

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

转载请注明出处,谢谢http://blog.csdn.net/harryweasley/article/details/46557827 因为项目中,有一个消息推送的功能,每次推送一个消息,就会开启FunctionActivity,那么为了避免重复开启它,在退后的时候,多次出现该Activity,就将该Activity的启动模式变为singleTask. 这样在之后的多次启动该Activity,便会调用onNewIntent(Intent intent)方法. activity通过intent传

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

AActivity跳转BActivity ,AActivity设置lauchmode = "SingleTask"的话,在getIntent无法获取BActivity里面的内容,无论是通过Intent跳转还是BActivity里面setResult后在AActivity的onActivityForResult中获取都不行.在网上查了下,通过重写如下方法,可以解决问题 protected void onNewIntent(Intent intent) { super.onNewInten

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

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4389674.html 需求:在ActivityA跳转到ActivityB,然后在ActivityB操作完返回数据给ActivityA. 这个很普遍的需求,一般情况是使用startActivityForResult的方式去完成. 但是当ActivityB为SingleTask时,这个方式就无效了.你会发现当你执行startActivityForResult后,o

android面试题-简答题(一)

1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 答:Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应.而Runnable是一个接口,Thread是Runnable的子类.所以说,他俩都算一个进程.HandlerThread顾名思义就是可以处理消息循环的线程,他是一个拥有Looper的线程,可以处理消息循环.与其说Handle

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

两个博客的维护着实费心,方便大家共同学习.督促.进步.感兴趣的伙伴们移步CSDN博客:http://blog.csdn.net/panhouye,博客目录如下: 1.Android中通过实现线程更新ProgressDialog(对话进度条) http://blog.csdn.net/panhouye/article/details/53300233 2.Android中的AlertDialog使用示例一(警告对话框) http://blog.csdn.net/panhouye/article/d

Activity Launch Mode 详解 singleTask正解

转载自:http://www.apkbus.com/android-17674-1-1.html Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance.以下逐一举例说明他们的区别: standard:Activity的默认加载方法,即使某个Activity在Task栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中.例如:现在栈的情况为:A B C D,在D这