解开Android应用程序组件Activity的"singleTask"之谜(2)

简介:
          再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考 在Ubuntu上下载、编译和安装Android最新源代码 一文。
       执行以下命令启动模拟器:

  
  
  1. USER-NAME@MACHINE-NAME:~/Android$ emulator   
        模拟器启动起,就可以App Launcher中找到Task应用程序图标,接着把它启动起来:

        点击中间的按钮,就会以"singleTask"的方式来启动SubActivity:

       现在,我们如何来确认SubActivity是不是在新的任务中启动并且位于这个新任务的堆栈底部呢?Android源代码工程为我们准备了adb工具,可以查看模拟器上系统运行的状况,执行下面的命令查看;


  
  
  1. USER-NAME@MACHINE-NAME:~/Android$ adb shell dumpsys activity 

      这个命令输出的内容比较多,这里我们只关心TaskRecord部分:


  
  
  1. Running activities (most recent first):   
  2.     TaskRecord{4070d8f8 #3 A shy.luo.task}   
  3.       Run #2: HistoryRecord{406a13f8 shy.luo.task/.SubActivity}   
  4.       Run #1: HistoryRecord{406a0e00 shy.luo.task/.MainActivity}   
  5.     TaskRecord{4067a510 #2 A com.android.launcher}   
  6.       Run #0: HistoryRecord{40677518 com.android.launcher/com.android.launcher2.Launcher}   

        果然,SubActivity和MainActivity都是运行在TaskRecord#3中,并且SubActivity在MainActivity的上面。这是怎么回事呢?碰到这种情况,Linus Torvalds告诫我们:Read the fucking source code;去年张麻子又说:枪在手,跟我走;我们没有枪,但是有source code,因此,我要说:跟着代码走。

        前面我们在两篇文章Android应用程序启动过程源代码分析Android应用程序内部启动Activity过程(startActivity)的源代码分析时,分别在Step 9和Step 8中分析了Activity在启动过程中与任务相关的函数ActivityStack.startActivityUncheckedLocked函数中,它定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:


  
  
  1. public class ActivityStack {   
  2.    
  3.     ......   
  4.    
  5.     final int startActivityUncheckedLocked(ActivityRecord r,   
  6.             ActivityRecord sourceRecord, Uri[] grantedUriPermissions,   
  7.             int grantedMode, boolean onlyIfNeeded, boolean doResume) {   
  8.         final Intent intent = r.intent;   
  9.         final int callingUid = r.launchedFromUid;   
  10.    
  11.         int launchFlags = intent.getFlags();   
  12.    
  13.         ......   
  14.    
  15.         ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)   
  16.            != 0 ? r : null;   
  17.    
  18.         ......   
  19.    
  20.         if (sourceRecord == null) {   
  21.             ......   
  22.         } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {   
  23.             ......   
  24.         } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE   
  25.            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {   
  26.             // The activity being started is a single instance...  it always   
  27.             // gets launched into its own task.   
  28.             launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;   
  29.         }   
  30.    
  31.         ......   
  32.    
  33.         boolean addingToTask = false;   
  34.         if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&   
  35.            (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)   
  36.            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK   
  37.            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {   
  38.                // If bring to front is requested, and no result is requested, and   
  39.                // we can find a task that was started with this same   
  40.                // component, then instead of launching bring that one to the front.   
  41.                if (r.resultTo == null) {   
  42.                    // See if there is a task to bring to the front.  If this is   
  43.                    // a SINGLE_INSTANCE activity, there can be one and only one   
  44.                    // instance of it in the history, and it is always in its own   
  45.                    // unique task, so we do a special search.   
  46.                    ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE   
  47.                        ? findTaskLocked(intent, r.info)   
  48.                        : findActivityLocked(intent, r.info);   
  49.                    if (taskTop != null) {   
  50.                           
  51.                        ......   
  52.    
  53.                        if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0   
  54.                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK   
  55.                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {   
  56.                                // In this situation we want to remove all activities   
  57.                                // from the task up to the one being started.  In most   
  58.                                // cases this means we are resetting the task to its   
  59.                                // initial state.   
  60.                                ActivityRecord top = performClearTaskLocked(   
  61.                                    taskTop.task.taskId, r, launchFlags, true);   
  62.                                if (top != null) {   
  63.                                    ......   
  64.                                } else {   
  65.                                    // A special case: we need to   
  66.                                    // start the activity because it is not currently   
  67.                                    // running, and the caller has asked to clear the   
  68.                                    // current task to have this activity at the top.   
  69.                                    addingToTask = true;   
  70.                                    // Now pretend like this activity is being started   
  71.                                    // by the top of its task, so it is put in the   
  72.                                    // right place.   
  73.                                    sourceRecord = taskTop;   
  74.                                }   
  75.                        } else if (r.realActivity.equals(taskTop.task.realActivity)) {   
  76.                            ......   
  77.                        } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {   
  78.                            ......   
  79.                        } else if (!taskTop.task.rootWasReset) {   
  80.                            ......   
  81.                        }   
  82.                           
  83.                        ......   
  84.                    }   
  85.                }   
  86.         }   
  87.    
  88.         ......   
  89.    
  90.         if (r.packageName != null) {   
  91.            // If the activity being launched is the same as the one currently   
  92.            // at the top, then we need to check if it should only be launched   
  93.            // once.   
  94.            ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);   
  95.            if (top != null && r.resultTo == null) {   
  96.                if (top.realActivity.equals(r.realActivity)) {   
  97.                    if (top.app != null && top.app.thread != null) {   
  98.                        ......   
  99.                    }   
  100.                }   
  101.            }   
  102.    
  103.         } else {   
  104.            ......   
  105.         }   
  106.    
  107.         boolean newTask = false;   
  108.    
  109.         // Should this be considered a new task?   
  110.         if (r.resultTo == null && !addingToTask   
  111.            && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {   
  112.              // todo: should do better management of integers.   
  113.                          mService.mCurTask++;   
  114.                          if (mService.mCurTask <= 0) {   
  115.                               mService.mCurTask = 1;   
  116.                          }   
  117.                          r.task = new TaskRecord(mService.mCurTask, r.info, intent,   
  118.                              (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);   
  119.                          if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r   
  120.                             + " in new task " + r.task);   
  121.                          newTask = true;   
  122.                          if (mMainStack) {   
  123.                               mService.addRecentTaskLocked(r.task);   
  124.                          }   
  125.         } else if (sourceRecord != null) {   
  126.            if (!addingToTask &&   
  127.                (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {   
  128.                 ......   
  129.            } else if (!addingToTask &&   
  130.                (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {   
  131.                 ......   
  132.            }   
  133.            // An existing activity is starting this new activity, so we want   
  134.            // to keep the new one in the same task as the one that is starting   
  135.            // it.   
  136.            r.task = sourceRecord.task;   
  137.               
  138.            ......   
  139.    
  140.         } else {   
  141.            ......   
  142.         }   
  143.    
  144.         ......   
  145.    
  146.         startActivityLocked(r, newTask, doResume);   
  147.         return START_SUCCESS;   
  148.     }   
  149.    
  150.     ......   
  151.    
  152. }   

 

 

        首先是获得用来启动Activity的Intent的Flags,并且保存在launchFlags变量中,这里,launcFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位没有置位,因此,notTop为null。

        接下来的这个if语句:


  
  
  1.    if (sourceRecord == null) {   
  2. ......   
  3.    } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {   
  4. ......   
  5.    } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE   
  6.       || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {   
  7. // The activity being started is a single instance...  it always   
  8. // gets launched into its own task.   
  9. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;   
  10.    }   

 

        这里变量r的类型为ActivityRecord,它表示即将在启动的Activity,在这个例子中,即为SubActivity,因此,这里的r.launchMode等于ActivityInfo.LAUNCH_SINGLE_TASK,于是,无条件将launchFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位置为1,表示这个SubActivity要在新的任务中启动,但是别急,还要看看其它条件是否满足,如果条件都满足,才可以在新的任务中启动这个SubActivity。
        接下将addingToTask变量初始化为false,这个变量也将决定是否要将SubActivity在新的任务中启动,从名字我们就可以看出,默认不增加到原有的任务中启动,即要在新的任务中启动。这里的r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK条成立,条件r.resultTo == null也成立,它表这个Activity不需要将结果返回给启动它的Activity。于是会进入接下来的if语句中,执行:


  
  
  1.   ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE   
  2. ? findTaskLocked(intent, r.info)   
  3. : findActivityLocked(intent, r.info)   

        这里的条件r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE成立,于是执行findTaskLocked函数,这个函数也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:


  
  
  1. public class ActivityStack {   
  2.    
  3.     ......   
  4.    
  5.     /**  
  6.     * Returns the top activity in any existing task matching the given  
  7.     * Intent.  Returns null if no such task is found.  
  8.     */   
  9.     private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {   
  10.         ComponentName cls = intent.getComponent();   
  11.         if (info.targetActivity != null) {   
  12.             cls = new ComponentName(info.packageName, info.targetActivity);   
  13.         }   
  14.    
  15.         TaskRecord cp = null;   
  16.    
  17.         final int N = mHistory.size();   
  18.         for (int i=(N-1); i>=0; i--) {   
  19.             ActivityRecord r = (ActivityRecord)mHistory.get(i);   
  20.             if (!r.finishing && r.task != cp   
  21.                 && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {   
  22.                     cp = r.task;   
  23.                     //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()   
  24.                     //        + "/aff=" + r.task.affinity + " to new cls="   
  25.                     //        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);   
  26.                     if (r.task.affinity != null) {   
  27.                         if (r.task.affinity.equals(info.taskAffinity)) {   
  28.                             //Slog.i(TAG, "Found matching affinity!");   
  29.                             return r;   
  30.                         }   
  31.                     } else if (r.task.intent != null   
  32.                         && r.task.intent.getComponent().equals(cls)) {   
  33.                             //Slog.i(TAG, "Found matching class!");   
  34.                             //dump();   
  35.                             //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);   
  36.                             return r;   
  37.                     } else if (r.task.affinityIntent != null   
  38.                         && r.task.affinityIntent.getComponent().equals(cls)) {   
  39.                             //Slog.i(TAG, "Found matching class!");   
  40.                             //dump();   
  41.                             //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);   
  42.                             return r;   
  43.                     }   
  44.             }   
  45.         }   
  46.    
  47.         return null;   
  48.     }   
  49.    
  50.     ......   
  51.    
  52. }   

        这个函数无非就是根据即将要启动的SubActivity的taskAffinity属性值在系统中查找这样的一个Task:Task的affinity属性值与即将要启动的Activity的taskAffinity属性值一致。如果存在,就返回这个Task堆栈顶端的Activity回去。在上面的AndroidManifest.xml文件中,没有配置MainActivity和SubActivity的taskAffinity属性,于是它们的taskAffinity属性值就默认为父标签application的taskAffinity属性值,这里,标签application的taskAffinity也没有配置,于是它们就默认为包名,即"shy.luo.task"。由于在启动SubActivity之前,MainActivity已经启动,MainActivity启动的时候,会在一个新的任务里面启动,而这个新的任务的affinity属性就等于它的第一个Activity的taskAffinity属性值。于是,这个函数会动回表示MainActivity的ActivityRecord回去.

        回到前面的startActivityUncheckedLocked函数中,这里的taskTop就表示MainActivity,它不为null,于是继续往前执行。由于条件r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK成立,于是执行下面语句:


  
  
  1. ActivityRecord top = performClearTaskLocked(   
  2. kTop.task.taskId, r, launchFlags, true);   

 

        函数performClearTaskLocked也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:


  
  
  1. public class ActivityStack {   
  2.    
  3.     ......   
  4.    
  5.     /**  
  6.     * Perform clear operation as requested by  
  7.     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the  
  8.     * stack to the given task, then look for  
  9.     * an instance of that activity in the stack and, if found, finish all  
  10.     * activities on top of it and return the instance.  
  11.     *  
  12.     * @param newR Description of the new activity being started.  
  13.     * @return Returns the old activity that should be continue to be used,  
  14.     * or null if none was found.  
  15.     */   
  16.     private final ActivityRecord performClearTaskLocked(int taskId,   
  17.     ActivityRecord newR, int launchFlags, boolean doClear) {   
  18.         int i = mHistory.size();   
  19.    
  20.         // First find the requested task.   
  21.         while (i > 0) {   
  22.             i--;   
  23.             ActivityRecord r = (ActivityRecord)mHistory.get(i);   
  24.             if (r.task.taskId == taskId) {   
  25.                 i++;   
  26.                 break;   
  27.             }   
  28.         }   
  29.    
  30.         // Now clear it.   
  31.         while (i > 0) {   
  32.             i--;   
  33.             ActivityRecord r = (ActivityRecord)mHistory.get(i);   
  34.             if (r.finishing) {   
  35.                 continue;   
  36.             }   
  37.             if (r.task.taskId != taskId) {   
  38.                 return null;   
  39.             }   
  40.             if (r.realActivity.equals(newR.realActivity)) {   
  41.                 // Here it is!  Now finish everything in front...   
  42.                 ActivityRecord ret = r;   
  43.                 if (doClear) {   
  44.                     while (i < (mHistory.size()-1)) {   
  45.                         i++;   
  46.                         r = (ActivityRecord)mHistory.get(i);   
  47.                         if (r.finishing) {   
  48.                             continue;   
  49.                         }   
  50.                         if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,   
  51.                             null"clear")) {   
  52.                                 i--;   
  53.                         }   
  54.                     }   
  55.                 }   
  56.    
  57.                 // Finally, if this is a normal launch mode (that is, not   
  58.                 // expecting onNewIntent()), then we will finish the current   
  59.                 // instance of the activity so a new fresh one can be started.   
  60.                 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE   
  61.                     && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {   
  62.                         if (!ret.finishing) {   
  63.                             int index = indexOfTokenLocked(ret);   
  64.                             if (index >= 0) {   
  65.                                 finishActivityLocked(ret, index, Activity.RESULT_CANCELED,   
  66.                                     null"clear");   
  67.                             }   
  68.                             return null;   
  69.                         }   
  70.                 }   
  71.    
  72.                 return ret;   
  73.             }   
  74.         }   
  75.    
  76.         return null;   
  77.     }   
  78.    
  79.     ......   
  80.    
  81. }   

        这个函数中作用无非就是找到ID等于参数taskId的任务,然后在这个任务中查找是否已经存在即将要启动的Activity的实例,如果存在,就会把这个Actvity实例上面直到任务堆栈顶端的Activity通过调用finishActivityLocked函数将它们结束掉。在这个例子中,就是要在属性值affinity等于"shy.luo.task"的任务中看看是否存在SubActivity类型的实例,如果有,就把它上面的Activity都结束掉。这里,属性值affinity等于"shy.luo.task"的任务只有一个MainActivity,而且它不是SubActivity的实例,所以这个函数就返回null了。







本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966135,如需转载请自行联系原作者
目录
相关文章
|
27天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
106 4
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
15天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14
|
18天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
16天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
15天前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
16天前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
24 0
|
1月前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
1月前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
1月前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。