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

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

执行以下命令启动模拟器:

USER-NAME@MACHINE-NAME:~/Android$ emulator

模拟器启动起,就可以App Launcher中找到Task应用程序图标,接着把它启动起来:

fb032e8bd042eb1fe58c2d97d631f2d8.png

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

4c2a793dd5f30778af55bc2a2a1df100.png

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

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

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

Running activities (most recent first):

TaskRecord{4070d8f8 #3 A shy.luo.task}

Run #2: HistoryRecord{406a13f8 shy.luo.task/.SubActivity}

Run #1: HistoryRecord{406a0e00 shy.luo.task/.MainActivity}

TaskRecord{4067a510 #2 A com.android.launcher}

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文件中:

publicclassActivityStack {

......

finalintstartActivityUncheckedLocked(ActivityRecord r,

ActivityRecord sourceRecord, Uri[] grantedUriPermissions,

intgrantedMode,booleanonlyIfNeeded,booleandoResume) {

finalIntent intent = r.intent;

finalintcallingUid = r.launchedFromUid;

intlaunchFlags = intent.getFlags();

......

ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)

!= 0? r :null;

......

if(sourceRecord ==null) {

......

} elseif(sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

......

} elseif(r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {

// The activity being started is a single instance...  it always

// gets launched into its own task.

launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;

}

......

booleanaddingToTask =false;

if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) !=0&&

(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

// If bring to front is requested, and no result is requested, and

// we can find a task that was started with this same

// component, then instead of launching bring that one to the front.

if(r.resultTo ==null) {

// See if there is a task to bring to the front.  If this is

// a SINGLE_INSTANCE activity, there can be one and only one

// instance of it in the history, and it is always in its own

// unique task, so we do a special search.

ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE

? findTaskLocked(intent, r.info)

: findActivityLocked(intent, r.info);

if(taskTop !=null) {

......

if((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) !=0

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

// In this situation we want to remove all activities

// from the task up to the one being started.  In most

// cases this means we are resetting the task to its

// initial state.

ActivityRecord top = performClearTaskLocked(

taskTop.task.taskId, r, launchFlags, true);

if(top !=null) {

......

} else{

// A special case: we need to

// start the activity because it is not currently

// running, and the caller has asked to clear the

// current task to have this activity at the top.

addingToTask = true;

// Now pretend like this activity is being started

// by the top of its task, so it is put in the

// right place.

sourceRecord = taskTop;

}

} elseif(r.realActivity.equals(taskTop.task.realActivity)) {

......

} elseif((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) ==0) {

......

} elseif(!taskTop.task.rootWasReset) {

......

}

......

}

}

}

......

if(r.packageName !=null) {

// If the activity being launched is the same as the one currently

// at the top, then we need to check if it should only be launched

// once.

ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);

if(top !=null&& r.resultTo ==null) {

if(top.realActivity.equals(r.realActivity)) {

if(top.app !=null&& top.app.thread !=null) {

......

}

}

}

} else{

......

}

booleannewTask =false;

// Should this be considered a new task?

if(r.resultTo ==null&& !addingToTask

&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {

// todo: should do better management of integers.

mService.mCurTask++;

if(mService.mCurTask <=0) {

mService.mCurTask = 1;

}

r.task = newTaskRecord(mService.mCurTask, r.info, intent,

(r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);

if(DEBUG_TASKS) Slog.v(TAG,"Starting new activity "+ r

+ " in new task "+ r.task);

newTask = true;

if(mMainStack) {

mService.addRecentTaskLocked(r.task);

}

} elseif(sourceRecord !=null) {

if(!addingToTask &&

(launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {

......

} elseif(!addingToTask &&

(launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {

......

}

// An existing activity is starting this new activity, so we want

// to keep the new one in the same task as the one that is starting

// it.

r.task = sourceRecord.task;

......

} else{

......

}

......

startActivityLocked(r, newTask, doResume);

returnSTART_SUCCESS;

}

......

}

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

接下来的这个if语句:

if(sourceRecord ==null) {

......

} elseif(sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

......

} elseif(r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE

|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {

// The activity being started is a single instance...  it always

// gets launched into its own task.

launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;

}

这里变量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语句中,执行:

ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE

? findTaskLocked(intent, r.info)

: findActivityLocked(intent, r.info)

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

publicclassActivityStack {

......

/**

* Returns the top activity in any existing task matching the given

* Intent.  Returns null if no such task is found.

*/

privateActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {

ComponentName cls = intent.getComponent();

if(info.targetActivity !=null) {

cls = newComponentName(info.packageName, info.targetActivity);

}

TaskRecord cp = null;

finalintN = mHistory.size();

for(inti=(N-1); i>=0; i--) {

ActivityRecord r = (ActivityRecord)mHistory.get(i);

if(!r.finishing && r.task != cp

&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

cp = r.task;

//Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()

//        + "/aff=" + r.task.affinity + " to new cls="

//        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);

if(r.task.affinity !=null) {

if(r.task.affinity.equals(info.taskAffinity)) {

//Slog.i(TAG, "Found matching affinity!");

returnr;

}

} elseif(r.task.intent !=null

&& r.task.intent.getComponent().equals(cls)) {

//Slog.i(TAG, "Found matching class!");

//dump();

//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);

returnr;

} elseif(r.task.affinityIntent !=null

&& r.task.affinityIntent.getComponent().equals(cls)) {

//Slog.i(TAG, "Found matching class!");

//dump();

//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);

returnr;

}

}

}

returnnull;

}

......

}

这个函数无非就是根据即将要启动的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成立,于是执行下面语句:

ActivityRecord top = performClearTaskLocked(

kTop.task.taskId, r, launchFlags, true);

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

publicclassActivityStack {

......

/**

* Perform clear operation as requested by

* {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the

* stack to the given task, then look for

* an instance of that activity in the stack and, if found, finish all

* activities on top of it and return the instance.

*

* @param newR Description of the new activity being started.

* @return Returns the old activity that should be continue to be used,

* or null if none was found.

*/

privatefinalActivityRecord performClearTaskLocked(inttaskId,

ActivityRecord newR, intlaunchFlags,booleandoClear) {

inti = mHistory.size();

// First find the requested task.

while(i >0) {

i--;

ActivityRecord r = (ActivityRecord)mHistory.get(i);

if(r.task.taskId == taskId) {

i++;

break;

}

}

// Now clear it.

while(i >0) {

i--;

ActivityRecord r = (ActivityRecord)mHistory.get(i);

if(r.finishing) {

continue;

}

if(r.task.taskId != taskId) {

returnnull;

}

if(r.realActivity.equals(newR.realActivity)) {

// Here it is!  Now finish everything in front...

ActivityRecord ret = r;

if(doClear) {

while(i 

i++;

r = (ActivityRecord)mHistory.get(i);

if(r.finishing) {

continue;

}

if(finishActivityLocked(r, i, Activity.RESULT_CANCELED,

null,"clear")) {

i--;

}

}

}

// Finally, if this is a normal launch mode (that is, not

// expecting onNewIntent()), then we will finish the current

// instance of the activity so a new fresh one can be started.

if(ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE

&& (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {

if(!ret.finishing) {

intindex = indexOfTokenLocked(ret);

if(index >=0) {

finishActivityLocked(ret, index, Activity.RESULT_CANCELED,

null,"clear");

}

returnnull;

}

}

returnret;

}

}

returnnull;

}

......

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值