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

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

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()方法的流程下图所示:

21.png

22.png

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


Ⅰ代码中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的状态,具体流程如下图所示:


23.png


Ⅱ调用代理的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了,其应用程序进程依然一直存在于设备中。

相关文章
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
2月前
|
存储 Android开发 开发者
深入理解安卓应用开发的核心组件
【10月更文挑战第8天】探索Android应用开发的精髓,本文带你了解安卓核心组件的奥秘,包括Activity、Service、BroadcastReceiver和ContentProvider。我们将通过代码示例,揭示这些组件如何协同工作,构建出功能强大且响应迅速的应用程序。无论你是初学者还是资深开发者,这篇文章都将为你提供新的视角和深度知识。
|
2月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
96 0
|
18天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
94 6
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
31 3
|
2月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
27 3
|
2月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
2月前
|
测试技术 数据库 Android开发
深入解析Android架构组件——Jetpack的使用与实践
本文旨在探讨谷歌推出的Android架构组件——Jetpack,在现代Android开发中的应用。Jetpack作为一系列库和工具的集合,旨在帮助开发者更轻松地编写出健壮、可维护且性能优异的应用。通过详细解析各个组件如Lifecycle、ViewModel、LiveData等,我们将了解其原理和使用场景,并结合实例展示如何在实际项目中应用这些组件,提升开发效率和应用质量。
52 6
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
20 0