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

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

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

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

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

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

       现在,我们如何来确认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,如需转载请自行联系原作者
上一篇:8、extern 解析与用法


下一篇:【RecyclerView】 十一、RecyclerView 数据更新 ( 删除单条数据 | 批量删除数据 )