解开Android应用程序组件Activity的"singleTask"之谜(2)
执行以下命令启动模拟器:
模拟器启动起,就可以App Launcher中找到Task应用程序图标,接着把它启动起来:
- USER-NAME@MACHINE-NAME:~/Android$emulator
点击中间的按钮,就会以"singleTask"的方式来启动SubActivity:
现在,我们如何来确认SubActivity是不是在新的任务中启动并且位于这个新任务的堆栈底部呢?Android源代码工程为我们准备了adb工具,可以查看模拟器上系统运行的状况,执行下面的命令查看;
- USER-NAME@MACHINE-NAME:~/Android$adbshelldumpsysactivity
这个命令输出的内容比较多,这里我们只关心TaskRecord部分:
- Runningactivities(mostrecentfirst):
- TaskRecord{4070d8f8#3Ashy.luo.task}
- Run#2:HistoryRecord{406a13f8shy.luo.task/.SubActivity}
- Run#1:HistoryRecord{406a0e00shy.luo.task/.MainActivity}
- TaskRecord{4067a510#2Acom.android.launcher}
- Run#0:HistoryRecord{40677518com.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(ActivityRecordr,
- ActivityRecordsourceRecord,Uri[]grantedUriPermissions,
- intgrantedMode,booleanonlyIfNeeded,booleandoResume){
- finalIntentintent=r.intent;
- finalintcallingUid=r.launchedFromUid;
- intlaunchFlags=intent.getFlags();
- ......
- ActivityRecordnotTop=(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){
- //Theactivitybeingstartedisasingleinstance...italways
- //getslaunchedintoitsowntask.
- 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){
- //Ifbringtofrontisrequested,andnoresultisrequested,and
- //wecanfindataskthatwasstartedwiththissame
- //component,theninsteadoflaunchingbringthatonetothefront.
- if(r.resultTo==null){
- //Seeifthereisatasktobringtothefront.Ifthisis
- //aSINGLE_INSTANCEactivity,therecanbeoneandonlyone
- //instanceofitinthehistory,anditisalwaysinitsown
- //uniquetask,sowedoaspecialsearch.
- ActivityRecordtaskTop=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){
- //Inthissituationwewanttoremoveallactivities
- //fromthetaskuptotheonebeingstarted.Inmost
- //casesthismeansweareresettingthetasktoits
- //initialstate.
- ActivityRecordtop=performClearTaskLocked(
- taskTop.task.taskId,r,launchFlags,true);
- if(top!=null){
- ......
- }else{
- //Aspecialcase:weneedto
- //starttheactivitybecauseitisnotcurrently
- //running,andthecallerhasaskedtoclearthe
- //currenttasktohavethisactivityatthetop.
- addingToTask=true;
- //Nowpretendlikethisactivityisbeingstarted
- //bythetopofitstask,soitisputinthe
- //rightplace.
- 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){
- //Iftheactivitybeinglaunchedisthesameastheonecurrently
- //atthetop,thenweneedtocheckifitshouldonlybelaunched
- //once.
- ActivityRecordtop=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;
- //Shouldthisbeconsideredanewtask?
- if(r.resultTo==null&&!addingToTask
- &&(launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0){
- //todo:shoulddobettermanagementofintegers.
- 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,"Startingnewactivity"+r
- +"innewtask"+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){
- ......
- }
- //Anexistingactivityisstartingthisnewactivity,sowewant
- //tokeepthenewoneinthesametaskastheonethatisstarting
- //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){
- //Theactivitybeingstartedisasingleinstance...italways
- //getslaunchedintoitsowntask.
- 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语句中,执行:
- ActivityRecordtaskTop=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{
- ......
- /**
- *Returnsthetopactivityinanyexistingtaskmatchingthegiven
- *Intent.Returnsnullifnosuchtaskisfound.
- */
- privateActivityRecordfindTaskLocked(Intentintent,ActivityInfoinfo){
- ComponentNamecls=intent.getComponent();
- if(info.targetActivity!=null){
- cls=newComponentName(info.packageName,info.targetActivity);
- }
- TaskRecordcp=null;
- finalintN=mHistory.size();
- for(inti=(N-1);i>=0;i--){
- ActivityRecordr=(ActivityRecord)mHistory.get(i);
- if(!r.finishing&&r.task!=cp
- &&r.launchMode!=ActivityInfo.LAUNCH_SINGLE_INSTANCE){
- cp=r.task;
- //Slog.i(TAG,"Comparingexistingcls="+r.task.intent.getComponent().flattenToShortString()
- //+"/aff="+r.task.affinity+"tonewcls="
- //+intent.getComponent().flattenToShortString()+"/aff="+taskAffinity);
- if(r.task.affinity!=null){
- if(r.task.affinity.equals(info.taskAffinity)){
- //Slog.i(TAG,"Foundmatchingaffinity!");
- returnr;
- }
- }elseif(r.task.intent!=null
- &&r.task.intent.getComponent().equals(cls)){
- //Slog.i(TAG,"Foundmatchingclass!");
- //dump();
- //Slog.i(TAG,"ForIntent"+intent+"bringingtotop:"+r.intent);
- returnr;
- }elseif(r.task.affinityIntent!=null
- &&r.task.affinityIntent.getComponent().equals(cls)){
- //Slog.i(TAG,"Foundmatchingclass!");
- //dump();
- //Slog.i(TAG,"ForIntent"+intent+"bringingtotop:"+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成立,于是执行下面语句:
- ActivityRecordtop=performClearTaskLocked(
- kTop.task.taskId,r,launchFlags,true);
函数performClearTaskLocked也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- publicclassActivityStack{
- ......
- /**
- *Performclearoperationasrequestedby
- *{@linkIntent#FLAG_ACTIVITY_CLEAR_TOP}:searchfromthetopofthe
- *stacktothegiventask,thenlookfor
- *aninstanceofthatactivityinthestackand,iffound,finishall
- *activitiesontopofitandreturntheinstance.
- *
- *@paramnewRDescriptionofthenewactivitybeingstarted.
- *@returnReturnstheoldactivitythatshouldbecontinuetobeused,
- *ornullifnonewasfound.
- */
- privatefinalActivityRecordperformClearTaskLocked(inttaskId,
- ActivityRecordnewR,intlaunchFlags,booleandoClear){
- inti=mHistory.size();
- //Firstfindtherequestedtask.
- while(i>0){
- i--;
- ActivityRecordr=(ActivityRecord)mHistory.get(i);
- if(r.task.taskId==taskId){
- i++;
- break;
- }
- }
- //Nowclearit.
- while(i>0){
- i--;
- ActivityRecordr=(ActivityRecord)mHistory.get(i);
- if(r.finishing){
- continue;
- }
- if(r.task.taskId!=taskId){
- returnnull;
- }
- if(r.realActivity.equals(newR.realActivity)){
- //Hereitis!Nowfinisheverythinginfront...
- ActivityRecordret=r;
- if(doClear){
- while(i<(mHistory.size()-1)){
- i++;
- r=(ActivityRecord)mHistory.get(i);
- if(r.finishing){
- continue;
- }
- if(finishActivityLocked(r,i,Activity.RESULT_CANCELED,
- null,"clear")){
- i--;
- }
- }
- }
- //Finally,ifthisisanormallaunchmode(thatis,not
- //expectingonNewIntent()),thenwewillfinishthecurrent
- //instanceoftheactivitysoanewfreshonecanbestarted.
- 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了。
更多相关文章
- 箭头函数的基础使用
- Python技巧匿名函数、回调函数和高阶函数
- android 视频通话 项目 源码
- findViewById()使用常见错误
- Android(安卓)- ListActivity 单击事件的响应
- Android应用程序启动过程源代码分析
- Android驱动(一)硬件访问服务学习之(一)Android通过JNI访问硬件
- android WebView总结
- Android(安卓)利用JNI调用Android(安卓)Java代码函数