深入剖析Android四大组件(八)——结束Activity的4个阶段

举报
择城终老 发表于 2021/07/26 23:56:54 2021/07/26
【摘要】 上一篇博文介绍了启动Activity请求的流程以及对相关数据结构的处理,那么当我们试图结束Activity的时候,ActivityManagerService的行为将会是怎样的呢?这一节将介绍结束Activity的3种主要方法和4个阶段。 1.结束Activity的3种主要方法 结束Activity时,我们通常采用如下3种主要方法。 ①以编程的方式结束Activity ...

上一篇博文介绍了启动Activity请求的流程以及对相关数据结构的处理,那么当我们试图结束Activity的时候,ActivityManagerService的行为将会是怎样的呢?这一节将介绍结束Activity的3种主要方法和4个阶段。


1.结束Activity的3种主要方法


结束Activity时,我们通常采用如下3种主要方法。


①以编程的方式结束Activity


该方法即在代码中显式调用Activity的finish()方法。一般来说,我们经常会遇到这样的需求——点击某个按钮退出界面,此时只需要在按钮的点击事件中添加finish()方法即可。finish()方法的代码如下所示,finish()内部默认调用finish(false):


private void finish(boolean finishTask) { if (mParent == null) { int resultCode;
 Intent resultData;
 synchronized (this) { resultCode = mResultCode;
 resultData = mResultData;
 } if (false) Log.v(TAG, "Finishing self: token=" + mToken);
 try { if (resultData != null) { resultData.prepareToLeaveProcess();
 } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData, finishTask)) { mFinished = true;
 } } catch (RemoteException e) { // Empty
 } } else { mParent.finishFromChild(this);
 }
}


以上红色标注的为主要方法。


②按键盘(硬键盘或者软键盘)上Back键来结束Activity


这种情况下,不需要添加任何代码就可以结束Activity,但需要注意的是,并不是所有的设备都会有Back键。在未加定制的Android代码中,它为每个Activity界面提供了软键盘。


当单击此按钮的时候,系统将会通过回调onBackPressed()方法告知Activity Back按键已经按下。onBackPressed()方法的代码如下所示:


public void onBackPressed() { if (mActionBar != null && mActionBar.collapseActionView()) { return;
 } if (!mFragments.getFragmentManager().popBackStackImmediate()) { finishAfterTransition();
 }
}


finishAfterTransition()代码如下:


public void finishAfterTransition() { if (!mActivityTransitionState.startExitBackTransition(this)) { finish();
 }
}


通过上面的代码可知,onBackPressed()方法的本质还是一个finish()方法。当然,也可以屏蔽这种行为,只需要在我们自行实现的Activity的onBackPressed()方法中取消调用super.onBackPressed()即可,但是我们不建议这样做。


③使用Home键使当前显示的Activity消失,回到Launcher首页


与Back键一样,并不是所有的设备都会提供硬Home按键。在未加定制的Android代码中,它为每个Activity界面提供了软键盘。


通常,应用程序无法捕获Home键,除非强行捕获,但不建议这么做。这个键将会由PhoneWindowManager处理,具体代码如下:


void startDockOrHome(){ Intent dock=createHomeDockIntent();
 if(dock!=null){ try{ mContext.startActivity(dock);
 return;
 }catch(ActivityNotFoundException e){ } } mContext.startActivity(mHomeIntent);
}


最终,Android会以mHomeIntent去启动Launcher从而使得当前Activity退居后台,Launcher被重新显示出来。mHomeIntent是这样定义的:


mHomeIntent=new Intent(Intent.ACTION_MAIN,null);

mHomeIntent.addCateGory(Intent.CATEGORY_HOME);

mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);


2.结束Activity的4个阶段


同启动Activity一样,结束Activity也有4个阶段,下面我们将对其进行详细讲解。


①第一阶段——参数初始化以及参数传递


与启动Activity相同,结束Activity同样需要ActivityManagerProxy将命令转发出去的。当按下Back键时,将会执行下面的这行代码:


ActivityManagerNative.getDefault().finishActivity(mToken,resultCode,resultData);


这时,ActivityManagerProxy会调用它的finishActivity()方法将参数写入Parcel中并转发出去。finishActivity()方法代码如下:


public boolean finishActivity(IBinder token,int resultCode,Intent resultData)throws RemoteException{ Parcel data=Parcel.obtain();
 Parcel reply=Parcel.obtain();
 ...... //转发指令
 mRemote.transact(FINISH_ACTIVITY_TRANSACTION,data,reply,0);
 ...... return res;
}


调用finishActivity()时,Android会回调ActivityManagerService的onTransact()方法,转而执行其基类(也就是ActivityManagerNative类)的onTransact()方法来向ActivityManagerService()发送请求:


@Override
public boolean onTransact(int code,Parcel data,Parcel reply,int flags)throws RemoteException{ ...... case FINISH_ACTIVITY_TRANSACTION:{ data.enforceInterface(IActivityManager.descriptor);
 IBinder token=data.readStrongBinder();
 Intent resultData=null;
 int resultCode=data.readInt();
 if(data.readInt()!=0){ resultData=Intent.CREATOR.createFromParcel(data);
 } boolean res=finishActivity(token,resultCode,resultData);
 reply.writeNoException();;
 reply.writeInt(res ? 1 : 0);
 return true;
 } ......
}


至此,参数处理以及指令发送的前驱工作已经完成,接下来的工作将由ActivityManagerService完成。


②第二阶段——获取需要结束的Activity的记录信息


在第二阶段,首先要做的是用ActivityManagerService调用ActivityStack的requestFinishActivityLocked()方法执行信息手机工作,具体代码如下所示:


public final boolean finishActivity(IBinder token,int resultCode,Intent resultData){ ...... final long origId= Binder.clearCallingIdentity();
 boolean res=mMainStack.requestFinishActivityLocked(token,resultCode,resultData,"app-request");
 Binder.restoreCallingIdentity(origId);
 return  res;
}


而在requestFinishActivityLocked()方法中,首先将使用indexOfTokenLocked()方法获取该Activity在启动Activity的历史记录(mHistory)中的偏移量,然后从启动历史记录中获取该Activity的记录信息(ActivityRecord),具体代码如下所示:


final boolean requestFinishActivityLocked(IBinder token,int resultCode,Intent resultData,String reason){ ....... //获取索引
 int index=indexOfTokenLocked(token);
 ...... //从历史记录中获取Activity记录信息
 ActivityRecord r=mHistory.get(index);
  //启动结束流程的下一个阶段
 finishActivityLocked(r,index,resultCode,resultData,reason);
 return  true;
}


其中indexOfTokenLocked()方法的关键代码如下所示:


final int indexOfTokenLocked(IBinder token){ ActivityRecord r=(ActivityRecord)token;
 return mHistory.indexOf(r);//获取需要结束的Activity在mHistory的所引值
 ......
}


③第三阶段——处理需要结束的Activity信息


在第三阶段中,我们已经获取到需要结束的Activity的记录信息,这里需要对它们进行一些处理,这通过finishActivityLocked()方法完成。finishActivityLocked()方法的流程下图所示:



该图有一下几点需要特别说明一下。


Ⅰ代码中r.makeFinishing()的作用是将Activity的正在结束标志置为true,并且将该Activity所在的Activity栈的Activity数量减一,这就为了后续操作做好了准备。makeFinishing()方法的代码如下所示:


void makeFinishing(){ if(!finishing){ finishing=true;//标识正在结束
 if(task!=null && inHistory){ task.numActivities--;//同一个栈的Activity数量减一
 } }
}


Ⅱ由于当前的Activity即将结束,它至少会被另一个Activity覆盖,这时当前Activity窗口则不应该继续将按键消息分发到当前Activity上。为完成这个需求,ActivityManagerService会调用pauseKeyDispatchingLocked()方法,该方法的代码如下所示:


void pauseKeyDispatchingLocked(){ if(!keyPaused){ keysPaused=true;
 service.mWindowManager.pauseKeyDispatching(this);
 }
}


Ⅲ假设使用startActivityForResult的方式启动当前Activity,那么结束此Activity时需要给调用的Activity传送处理的结果。这里使用如下代码完成:


resultTo.addResultLocked(r,r.resultWho,r.requestCode,resultCode,resultData);


下面我们来看看addResultLocked()方法的行为,具体如下代码所示:


void addResultLocked(ActivityRecord from,String resultWho,int requestCode,int resultCode,Intent resultData){ Instrumentation.ActivityResult r=new Instrumentation.ActivityResult(from,resultWho,requestCode,resultCode,resultData);
 if(results==null){ results=new ArrayList();
 } results.add(r);
}


④第四阶段——Activity间调度准备


在第三阶段中,我们完成了对一些Activity栈以及窗口的处理,为Activity调度做了一些准备工作,然后启动了ActivityThread的调度流程:


startPausingLocked(false,false);


其中startPausingLocked()方法的关键代码如下所示:


private final void startPausingLocked(boolean userLeaving,boolean uiSleeping){ ..... mResumedActivity=null;
 mPausingActivity=prev;
 mLastPausedActivity=prev;
 prev.state=ActivityState.PAUSING;
 prev.task.touchActiveTime();
 prev.updateThumbnail(screenshotActivities(prev),null);
 ..... prev.app.thread.schedulePauseActivity(prev,prev.finishing,userleaving,pre.configChangeFlags);
}


通过学习启动Activity的4个阶段,我们知道ActivityStack启动了ActivityThread的Activity间的调度,这里就要讲解当Activity将要被结束时Activity之间的调度行为。schedulePauseActivity()方法用于完成这个任务,其代码如下所示:


public final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving,int configChanges){ queueOrSendMessage( finished? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,token,
 (userLeaving?1:0),
 configChanges);
}


此处,queueOrSendMessage()方法拼装了一个消息,发往用于处理消息的handler。在Handler中,最终调用handlePauseActivity()方法处理这个消息。handlePauseActivity()方法的代码如下所示:


private void handlePauseActivity(IBinder token,boolean finished,
 boolean userLeaving,int configChanges){ ActivityClientRecord r=mActivities.get(token);
 ...... //暂停当前的Activity
 performPauseActivity(token,finished,r.isPreHoneycomb());
  //通知服务操作完成
 ActivityManagerNative.getDefault().activityPaused(token);
}


该方法主要完成以下两件事情。


Ⅰ调用performPauseActivity()方法回调Activity的onPause等回调接口,并设置Activity的状态,具体流程如下图所示:



Ⅱ调用代理的activityPaused()方法,转发Activity的一个pause指令到Activity管理服务,代码如下所示:


public boolean onTransact(int code,Parcel data,Parcel reply,int flags)throws RemoteException{ case ACTIVITY_PAUSED_TRANSACTION:{ ...... IBinder token=data.readStrongBinder();
 activityPaused(token);
 ...... return true;
 }
}


Activity管理服务的activityPaused()方法主要完成该Activity其他生命周期的调度以及恢复前一个Activity,其中的部分关键代码如下所示:


private final void completePauseLocked(){ ActivityRecord prev=mPausingActivity;
 ...... prev=finishCurrentActivityLocked(prev,FINISH_AFTER_VISIBLE);
 ..... destroyActivityLocked(prev,true,false);
 ..... resumeTopActivityLocked(prev);//恢复一个Activity
  if(prev !=null){ //允许窗口分发按键消息到此Activity(prev)
 prev.resumeKeyDispatchingLocked();
 } ..... prev.cpuTimeAtResume=0;//重置
}


当前一个Activity被重新显示出来的时候,它需要具有捕获按键消息的能力,因此这里调用了resumeKeyDispatchingLocked()方法来完成这个需求。resumeKeyDispatchingLocked()方法的作用是恢复对这个Activity的按键分发,具体代码如下所示:


void resumeKeyDispatchingLocked(){ if(keysPaused){ keysPaused=false;
 service.mWindowManager.resumeKeyDispatching(false);
 }
}


至此,原来显示的Activity由于按下了Back键而消失,而覆盖在它下面的那个Activity则被重新显示出来了。


注意1:按Home键与按下Back键或以Activity的finish()方法结束Activity不同是的,按Home键是强制显示Launcher而使得其他Activity被Launcher覆盖,而按下Back键或以Activity的finish()方法结束Activity,则是因为当前的Activity消失而导致在它下面的Activity被显示出来。它们有着本质的区别,请注意这个注意。


注意2:只要应用程序启动,进程将会被保留,除非应用程序发生了严重的异常或者使用别的工具(比如DDMS)杀掉进程,就算我们按下Back键结束Activity了,其应用程序进程依然一直存在于设备中。



文章来源: liyuanjinglyj.blog.csdn.net,作者:李元静,版权归原作者所有,如需转载,请联系作者。

原文链接:liyuanjinglyj.blog.csdn.net/article/details/49976877

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。