从源码角度看finish()方法的执行流程
Posted absfree
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从源码角度看finish()方法的执行流程相关的知识,希望对你有一定的参考价值。
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()方法执行流程
1 /** 2 * Finishes the current activity and specifies whether to remove the task associated with this 3 * activity. 4 */ 5 private void finish(boolean finishTask) { 6 if (mParent == null) { 7 int resultCode; 8 Intent resultData; 9 synchronized (this) { 10 resultCode = mResultCode; 11 resultData = mResultData; 12 } 13 if (false) Log.v(TAG, "Finishing self: token=" + mToken); 14 try { 15 if (resultData != null) { 16 resultData.prepareToLeaveProcess(); 17 } 18 if (ActivityManagerNative.getDefault() 19 .finishActivity(mToken, resultCode, resultData, finishTask)) { 20 mFinished = true; 21 } 22 } catch (RemoteException e) { 23 // Empty 24 } 25 } else { 26 mParent.finishFromChild(this); 27 } 28 }
根据注释我们可以知道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方法如下所示:
1 public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask) 2 throws RemoteException { 3 Parcel data = Parcel.obtain(); 4 Parcel reply = Parcel.obtain(); 5 data.writeInterfaceToken(IActivityManager.descriptor); 6 data.writeStrongBinder(token); 7 data.writeInt(resultCode); 8 if (resultData != null) { 9 data.writeInt(1); 10 resultData.writeToParcel(data, 0); 11 } else { 12 data.writeInt(0); 13 } 14 data.writeInt(finishTask ? 1 : 0); 15 mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0); 16 reply.readException(); 17 boolean res = reply.readInt() != 0; 18 data.recycle(); 19 reply.recycle(); 20 return res; 21 }
以上代码中的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中),所以我们接着看这个方法的实现:
1 /** 2 * This is the internal entry point for handling Activity.finish(). 3 * 4 * @param token The Binder token referencing the Activity we want to finish. 5 * @param resultCode Result code, if any, from this Activity. 6 * @param resultData Result data (Intent), if any, from this Activity. 7 * 8 * @return Returns true if the activity successfully finished, or false if it is still running. 9 */ 10 public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) { 11 // Refuse possible leaked file descriptors 12 if (resultData != null && resultData.hasFileDescriptors() == true) { 13 throw new IllegalArgumentException("File descriptors passed in Intent"); 14 } 15 synchronized(this) { 16 ...33 final long origId = Binder.clearCallingIdentity(); 17 boolean res = requestFinishActivityLocked(token, resultCode, 18 resultData, "app-request"); 19 Binder.restoreCallingIdentity(origId); 20 return res; 21 } 22 }
根据源码中的注释我们可以看到,这个方法是处理Activity.finish()的内部入口点。这个方法内部又调用了requestFinishActivityLocked方法,所以我们还要接着跟进。requestFinishActivityLocked方法的源码如下:
1 private final boolean requestFinishActivityLocked(IBinder token, int resultCode, 2 Intent resultData, String reason) { 3 ... 4 HistoryRecord r = (HistoryRecord)mHistory.get(index); 5 // Is this the last activity left? 6 boolean lastActivity = true; 7 for (int i=mHistory.size()-1; i>=0; i--) { 8 HistoryRecord p = (HistoryRecord)mHistory.get(i); 9 if (!p.finishing && p != r) { 10 lastActivity = false; 11 break; 12 } 13 } 14 15 // If this is the last activity, but it is the home activity, then 16 // just don‘t finish it. 17 if (lastActivity) { 18 if (r.intent.hasCategory(Intent.CATEGORY_HOME)) { 19 return false; 20 } 21 } 22 23 finishActivityLocked(r, index, resultCode, resultData, reason); 24 return true; 25 }
首先我们看一下第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()方法的执行流程的主要内容,如果未能解决你的问题,请参考以下文章
redis源码学习从源码角度看主从复制:全量同步 && 部分同步
Android图片加载框架最全解析,从源码的角度理解Glide的执行流程