回到前面的startActivityUncheckedLocked函数中,这里的变量top就为null了,于是执行下面的else语句:

  1. if (top != null) {
  2. ......
  3. } else {
  4. // A special case: we need to
  5. // start the activity because it is not currently
  6. // running, and the caller has asked to clear the
  7. // current task to have this activity at the top.
  8. addingToTask = true;
  9. // Now pretend like this activity is being started
  10. // by the top of its task, so it is put in the
  11. // right place.
  12. sourceRecord = taskTop;
  13. }

于是,变量addingToTask值就为true了,同时将变量sourceRecord的值设置为taskTop,即前面调用findTaskLocked函数的返回值,这里,它就是表示MainActivity了。

继续往下看,下面这个if语句:

  1. if (r.packageName != null) {
  2. // If the activity being launched is the same as the one currently
  3. // at the top, then we need to check if it should only be launched
  4. // once.
  5. ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
  6. if (top != null && r.resultTo == null) {
  7. if (top.realActivity.equals(r.realActivity)) {
  8. if (top.app != null && top.app.thread != null) {
  9. ......
  10. }
  11. }
  12. }
  13. } else {
  14. ......
  15. }

它是例行性地检查当前任务顶端的Activity,是否是即将启动的Activity的实例,如果是否的话,在某些情况下,它什么也不做,就结束这个函数调用了。这里,当前任务顶端的Activity为MainActivity,它不是SubActivity实例,于是继续往下执行:

  1. boolean newTask = false;
  2. // Should this be considered a new task?
  3. if (r.resultTo == null && !addingToTask
  4. && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
  5. ......
  6. } else if (sourceRecord != null) {
  7. if (!addingToTask &&
  8. (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
  9. ......
  10. } else if (!addingToTask &&
  11. (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
  12. ......
  13. }
  14. // An existing activity is starting this new activity, so we want
  15. // to keep the new one in the same task as the one that is starting
  16. // it.
  17. r.task = sourceRecord.task;
  18. ......
  19. } else {
  20. ......
  21. }

这里首先将newTask变量初始化为false,表示不要在新的任务中启动这个SubActivity。由于前面的已经把addingToTask设置为true,因此,这里会执行中间的else if语句,即这里会把r.task设置为sourceRecord.task,即把SubActivity放在MainActivity所在的任务中启动。

最后,就是调用startActivityLocked函数继续进行启动Activity的操作了。后面的操作这里就不跟下去了,有兴趣的读者可以参考两篇文章Android应用程序启动过程源代码分析和Android应用程序内部启动Activity过程(startActivity)的源代码分析。

到这里,思路就理清了,虽然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"之谜,还是要自力更生啊,不过,如果我们仔细阅读官方文档,在http://developer.android.com/guide/topics/manifest/activity-element.html中,有这样的描述:

As shown in the table above, standard is the default mode and is appropriate for most types of activities. SingleTop is also a common and useful launch mode for many types of activities. The other modes — singleTask and singleInstance —are not appropriate for most applications, since they result in an interaction model that is likely to be unfamiliar to users and is very different from most other applications.
        Regardless of the launch mode that you choose, make sure to test the usability of the activity during launch and when navigating back to it from other activities and tasks using the BACK key.

这样看,官方文档也没有坑我们呢,它告诫我们:make sure to test the usability of the activity during launch

解开Android应用程序组件Activity的singleTask之谜(3)相关推荐

  1. Android应用程序组件Content Provider的启动过程源代码分析(1)

             通过前面的学习,我们知道在Android系统中,Content Provider可以为不同的应用程序访问相同的数据提供统一的入口.Content Provider一般是运行在独立的进 ...

  2. Android应用程序组件Content Provider的共享数据更新通知机制分析

    在Android系统中,应用程序组件Content Provider为不同的应用程序实现数据共享提供了基础设施,它主要通过Binder进程间通信机制和匿名共享内存机制来实现的.关于数据共享的另一个 话 ...

  3. Android应用程序组件Content Provider的启动过程源代码分析(6)

        Step 17. ActivityThread.installProvider         这个函数定义在frameworks/base/core/java/android/app/Act ...

  4. Android应用程序组件Content Provider的共享数据更新通知机制分析(3)

            3. 数据更新通知的发送过程        在前面这篇文章Android应用程序组件Content Provider应用实例介绍的应用程序Acticle中,当调用ArticlesAda ...

  5. Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(1)

             在Android系统中,不同的应用程序是不能直接读写对方的数据文件的,如果它们想共享数据的话,只能通过Content Provider组件来实现.那么,Content Provide ...

  6. Android应用程序组件Content Provider简要介绍和学习计划

    在Android系统中,Content Provider作为应用程序四大组件之一,它起到在应用程序之间共享数据的作用,同时,它还是标准的数据访问接口.前面的一系列文章已经分析过Android应用程序的 ...

  7. Android应用程序组件

    如果你想从事Android应用程序开发,那么了解Android应用程序的思想是非常必要的,Android没有应用程序的统一入口(例如Main()方法),各个应用之间是相互独立的,并且运行在自己的进程当 ...

  8. Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(2)

        Step 7. ContentProviderProxy.query       这个函数定义在frameworks/base/core/java/android/content/Conten ...

  9. Android之四大组件(Activity)

    Activity简述 Activity是Android应用中负责与用户交互的组件.在应用中创建自己的Activity需要继承Activity或者继承Activity的 子类. public class ...

最新文章

  1. 【经验】对一个合格C++高级工程师(音视频方向)的要求
  2. python读取大文件csv内存溢出_Python,内存错误,csv文件太大
  3. 蓝桥杯比赛常考算法_蓝桥杯总结-常用函数及算法
  4. java并发性是指什么_java – 什么是“非阻塞”并发,它与普通并发性有什么不同?...
  5. opencv cv2.LUT()(使用查找表中的值填充输出数组)
  6. 用最快速度,打造「最强 Webpack 前端工具链」,强势运行
  7. 本周耐撕团队个人总结
  8. java blockqueue_[Java基础] Java多线程-工具篇-BlockingQueue
  9. Hadoop概述--四大组件架构及其关系
  10. Android Activity防劫持方案
  11. webdav支持的网盘对比分析
  12. Win7 局域网共享文件夹
  13. 干货 | 携程数据血缘构建及应用
  14. Scrum立会报告+燃尽图(Beta阶段第二周第三次)
  15. 北大心理与认知学院院长方方:人类注意力图和动态机制
  16. MathWorks官方MATLAB/Simulink基础入门视频教程 笔记(Simulink基础)
  17. 如果你是淘宝的产品经理,你该如何设计淘宝?
  18. 移动硬盘在计算机中不显示数据能恢复,移动硬盘摔了出现无法读取数据还能恢复吗?...
  19. 泛函分析 02.02 赋范空间-完备的赋范空间
  20. Java如何从入门进阶到架构师

热门文章

  1. 千万别从网页复制粘贴命令
  2. 【Tomcat】Tomcat 系统架构与设计模式,第 2 部分: 设计模式分析
  3. 无线SD-WAN提供商Cradlepoint完成C轮融资8900万美元
  4. 数据中心高速需求 推动光通信迈向100Gbps
  5. 大道至简读书笔记(1)
  6. 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁
  7. Windows Phone 7 button控件
  8. PAT 1084. 外观数列 (20) - 乙级
  9. 蓝桥杯 ADV-155 算法提高 上帝造题五分钟
  10. plsql初始错误sql.net未正确安装_ANSYS | ansys18.0完整安装过程及常见问题解决方案[图文]...