1. finish()方法概览

首先我们来看一下finish方法的无参版本的定义:

/**
* Call this when your activity is done and should be closed. The
* ActivityResult is propagated back to whoever launched you via
* onActivityResult(). */
public void finish() {
finish(false);
}

根据源码中的注释我们可以知道,当我们的activity已经完成它的工作,我们想要关闭它时,我们可以调用finish()方法。这个方法内部回去调用finish(boolean)方法,并传入false作为参数。那么接下来我们从finish(boolean)方法出发,来了解下finish()方法的大致执行流程。

2. finish()方法执行流程

     /**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
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);
}
}

根据注释我们可以知道boolean类型参数finishTask的作用是是否移除与我们要关闭的activity相关联的task。我们注意到这个方法的访问修饰符是private,所以它只是供Activity类内部调用的方法。那么我们接下来看一下这个方法究竟是怎样实现关闭一个activity的。在第6行,我们判断mParent是否为null。现在我们只需要知道mParent在一般情况下均为null即可。所以这个方法接下来会执行第7到24行的代码。在第10行和第11行,我们分别把resultCode和resultData赋值为mResultCode和mResultData。resultCode和resultData最终会传入到onActivityResult方法中。

以上代码的关键在于第18行调用的finishActivity方法,若这个方法返回true,我们就会设置Activity的mFinished成员变量为true。isFinishing()方法用于判断一个Activity是否处于销毁状态,这个方法的实现就是返回mFinished成员变量。也就是说,若finishActivity方法执行完毕并返回true,则Activity就被成功销毁了。下面我们回到第18行:通过调用ActivityManagerNative.getDefault()方法会得到一个ActivityManagerProxy对象,这是ActivityManagerService(下文简称为AMS)的代理对象。那么AMS是什么呢?这里我们只需要知道它是一个系统服务,系统中四大组件的启动、切换、销毁等都是由它负责的。我们通过ActivityManagerProxy对象可以请求AMS去启动、暂停或是销毁一个Activity。ActivityManagerProxy是ActivityManagerNative的一个内部类,它的finishActivity方法如下所示:

     public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeInt(resultCode);
if (resultData != null) {
data.writeInt(1);
resultData.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
data.writeInt(finishTask ? 1 : 0);
mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
return res;
}

以上代码中的reply用于保存返回结果,data会作为参数传递给AMS。我们首先向要传递给AMS的data中写入了token、resultCode。若resultData不为null,则会先写入一个整数1,再写入resultData,若resultData为null,则只写入一个整数0。然后我们再根据finishTask参数为true或false分别写入1或0。以上代码的关键在于第15行调用的mRemote.transact(...)方法,它会导致AMS的onTransact(...)方法被调用。AMS的onTransact方法调用了ActivityManagerNative的onTransact方法,这个方法的代码如下:

    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
...
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 finishTask = (data.readInt() != 0);
boolean res = finishActivity(token, resultCode, resultData, finishTask);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
}
...
}

我们可以看到,这个方法会根据传入的code参数执行不同的case分支,这里会执行的是code为FINISH_ACTIVITY_TRANSACTION的分支。以上代码中又调用了finishActivity方法(AMS中),所以我们接着看这个方法的实现:

     /**
* This is the internal entry point for handling Activity.finish().
*
* @param token The Binder token referencing the Activity we want to finish.
* @param resultCode Result code, if any, from this Activity.
* @param resultData Result data (Intent), if any, from this Activity.
*
* @return Returns true if the activity successfully finished, or false if it is still running.
*/
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) {
// Refuse possible leaked file descriptors
if (resultData != null && resultData.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
... final long origId = Binder.clearCallingIdentity();
boolean res = requestFinishActivityLocked(token, resultCode,
18 resultData, "app-request");
19 Binder.restoreCallingIdentity(origId);
20 return res;
21 }
22 }

根据源码中的注释我们可以看到,这个方法是处理Activity.finish()的内部入口点。这个方法内部又调用了requestFinishActivityLocked方法,所以我们还要接着跟进。requestFinishActivityLocked方法的源码如下:

     private final boolean requestFinishActivityLocked(IBinder token, int resultCode,
Intent resultData, String reason) {
...
HistoryRecord r = (HistoryRecord)mHistory.get(index);
// Is this the last activity left?
boolean lastActivity = true;
for (int i=mHistory.size()-1; i>=0; i--) {
HistoryRecord p = (HistoryRecord)mHistory.get(i);
if (!p.finishing && p != r) {
lastActivity = false;
break;
}
} // If this is the last activity, but it is the home activity, then
// just don't finish it.
if (lastActivity) {
if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
return false;
}
} finishActivityLocked(r, index, resultCode, resultData, reason);
return true;
}

首先我们看一下第4行出现了HistoryRecord和mHistory。这里我们简单的介绍一下它们两个。mHistory是AMS的成员变量,它是一个ArrayList,里面存储的元素类型为HistoryRecord,一个HistoryReord表示一个Activity。也就是说mHistory存储了系统中所有曾经存在过或正在运行的Activity。第6行到第13行的功能就是判断现在系统中是否只剩下一个正在运行Activity,这一点从注释中就可以看出。然后在第17行到第21行,若唯一正在运行的Activity是HomeActivity,则直接返回false(不会关闭它)。以上代码中的关键在于第23行调用的finishActivityLocked方法,这个方法的核心代码如下:

    private final boolean finishActivityLocked(HistoryRecord r, int index,
int resultCode, Intent resultData, String reason) { ...if (mResumedActivity == r) {
...
} else if (r.state != ActivityState.PAUSING) {
// If the activity is PAUSING, we will complete the finish once
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
return finishCurrentActivityLocked(r, index,
FINISH_AFTER_PAUSE) == null;
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
}
return false;
}

以上代码实际上会调用finishCurrentActivityLocked方法,若这个方法返回值为null,finishActivityLocked则会返回true,否则会返回false。那么我们来看看finishCurrentActivityLocked方法的实现:

    private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
int index, int mode) {
... final ActivityState prevState = r.state;
r.state = ActivityState.FINISHING;
if (mode == FINISH_IMMEDIATELY
|| prevState == ActivityState.STOPPED
|| prevState == ActivityState.INITIALIZING) {
// If this activity is already stopped, we can just finish
// it right now.
return destroyActivityLocked(r, true) ? null : r;
} else {
// Need to go through the full pause cycle to get this
// activity into the stopped state and then finish it.
if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
mFinishingActivities.add(r);
resumeTopActivityLocked(null);
}
return r;
}

根据源码中的注释我们可以知道,若Activity已经处于停止状态,则可以立即调用destroyActivityLocked方法;否则我们需要先经历完整的“暂停周期”以让这个Activity处于停止状态后再结束它。

在destroyActivityLocked方法中存在如下这句代码:

r.app.thread.scheduleDestroyActivity(r, r.finishing,r.configChangeFlags);

其中,r.app.thread实际上是AMS持有的应用程序进程的ApplicationThread的代理对象,所以实际上调用的是ApplicationThread的scheduleDestroyActivity方法,而后这个方法中会向主线程(ActivityThread)发送一个H.DESTROY_ACTIVITY消息,主线程会调用handleDestroyActivity来处理这个消息,再经过层层调用后,Activity的onDestroy方法会被回调。对这一过程感兴趣的同学可以自行阅读相关源码。

3. finish()方法总结

通过上面对源码的分析,我们大概了解了finish()方法的工作流程,有两点需要我们注意:

(1)经过层层调用,ApplicationThread.scheduleDestroyActivity方法会被调用,这个方法会完成对Activity的销毁工作,并且会回调Activity.onDestroy()方法。所以我们知道了调用finish()方法会导致对Acitivity的销毁,从而导致Activity.onDestroy()被回调。

(2)在我们的日常开发中,往往会将finish()方法的调用包含在Activity的某个生命周期中,而实际上Activity的各个生命周期方法都是由ApplicationThread.scheduleXXXActivity方法回调的,这个方法会向主线程发送一个H.XXX_ACTIVITY消息,随后主线程的Looper会从消息队列中这个消息并调用handleXXXActivity方法来处理它,并最终会回到Activity.onXXX方法。比如我们在onCreate()方法中调用了finish()方法,那么由于此时主线程正处于对H.CREATE_ACTIVITY消息的处理中,所以暂时无法处理H.DESTROY_ACTIVITY消息,只有当主线程处理完前一个消息了,才会着手处理H_DESTROY_ACTIVITY消息。因此,我们调用finish()方法后,onDestroy()往往并不会被立即调用,但是我们可以通过isFinishing()方法来判断Activity是否处于销毁状态。

以上是我对finish()方法相关源码的执行流程的总结,由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出,谢谢大家:)

4. 参考资料

https://android.googlesource.com/platform/frameworks/base/+/android-6.0.1_r26/core/java/android/app

从源码角度看finish()方法的执行流程的更多相关文章

  1. 从JDK源码角度看Short

    概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...

  2. 从JDK源码角度看Byte

    Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...

  3. 从JDK源码角度看Object

    Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...

  4. 从JDK源码角度看Boolean

    Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...

  5. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...

  6. Android布局性能优化—从源码角度看ViewStub延迟加载技术

    在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码 ...

  7. 从源码角度看JedisPoolConfig参数配置

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 你好,JedisPoolConfig Java中使用Jedis作为连接Redis的工具.在使用Jedis的也可以配置Jed ...

  8. 从源码角度看 PHP 字符串类型转换

    PHP 的类型转换是比较方便的,但是越是容易使用的东西,底层的实现越是复杂,而且在使用中像我这样的新手也往往不清楚转换后的结果到底是什么.有时候,对于 Java 这种强类型的语言,使用的时候需要强制进 ...

  9. 从源码角度看LinkedList一些基本操作(jdk1.7)

    介绍 LinkedList是一个双向链表,就像下图展示那样,每个节点有个指向上个元素和一个指向下个元素的指针. 接下来我会对我们经常使用的方法进行介绍,代码如下 @Test public void t ...

随机推荐

  1. ORACLE口令管理

    口令文件介绍 在ORALCE数据库系统中,用户如果要以特权用户身份(SYS/SYSDBA/SYSOPER)登录ORALCE数据库可以有两种身份验证的方法:即使用与操作系统集成的身份验证或使用ORALC ...

  2. Spring CharacterEncodingFilter

    <!-- 配置请求过滤器,编码格式设为UTF-8,避免中文乱码--> <filter> <filter-name>springUtf8Encoding</fi ...

  3. linux 拨号+squid监控脚本

    客户端 #!/bin/bash #get_memory-info a=`free -m|grep Mem|awk '{print$2}'` #total-memory b=`free -m|grep ...

  4. c# ref 的作用

    Usage of ref keyword in C#  When we pass a value type variable as a parameter, then it passes its va ...

  5. 使用jquery合并表格中相同文本的相邻单元格

    一.效果 二.代码 <!DOCTYPE HTML> <html> <head> <title>Example</title> <met ...

  6. Spring学习之AOP总结帖

    AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组 ...

  7. 【温故而知新-Javascript】使用canvas元素(第一部分)

    1. 开始使用 canvas 元素 canvas 元素非常简单,这是指它所有的功能都体现在一个JavaScript对象上,因此该元素本身只有两个属性:width 和 height. canvas 元素 ...

  8. namesilo域名注册教程

    一.注册账号 打开http://www.namesilo.com ,我们先去注册一个Namesilo帐号,然后再在Namesilo注册域名!如图: 接下来,就是填写一些简单资料,如图: 然后Names ...

  9. 译:Google的大规模集群管理工具Borg(一)------ 用户视角的Borg特性

    概述 Google的Borg系统是一个集群管理工具,在它上面运行着成千上万的job,这些job来自许许多多不同的应用,并且跨越多个集群,而每个集群又由大量的机器构成. Borg通过组合准入控制,高效的 ...

  10. jquery.roundabout.js实现3D图片层叠旋转木马切换

    最近项目中需要实现3D图片层叠旋转木马切换的效果,于是用到了jquery.roundabout.js. 兼容性如图: html结构代码: <div id="featured-area& ...