从源码角度看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

时间: 2024-12-09 16:41:27

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

从源码角度看Android系统SystemServer进程启动过程

copy frome :https://blog.csdn.net/salmon_zhang/article/details/93208135 SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动过程以及SystemServer进程做了哪些重要工作. 1. SystemServer进程启动的起点从<从源码角度看Android系统Zygote进程启动过

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

在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码中通过控制View.VISIABLE动态的更改它的可见性.这样的做法的优点是逻辑简单而且控制起来比较灵活.但是它的缺点就是,耗费资源,虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性.也就是说,会

从JDK源码角度看java并发的原子性如何保证

JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面,肯定要通过用C++编写的native本地方法来扩展实现.JDK提供了一个类来满足CAS的要求,sun.misc.Unsafe,从名字上可以大概知道它用于执行低级别.不安全的操作,AQS就是使用此类完成硬件级别的原子操作. Unsafe是一个很强大的类,它可以分配内存.释放内存.可以定位对象某字段的

从JDK源码角度看并发的公平性

JAVA为简化开发者开发提供了很多并发的工具,包括各种同步器,有了JDK我们只要学会简单使用类API即可.但这并不意味着不需要探索其具体的实现机制,本文从JDK源码角度简单讲讲并发时线程竞争的公平性. 所谓公平性指所有线程对临界资源申请访问权限的成功率都一样,不会让某些线程拥有优先权.我们知道CLH Node FIFO等待队列是一个先进先出的队列,那么是否就可以说每条线程获取锁时就是公平的呢?关于公平性这里分拆成三个点分别阐述: ① 准备入队列的节点,此情况讨论的是线程加入等待队列时产生的竞争是

从源码角度看一个apk的启动过程和一个activity的启动过程

APK程序的运行过程 首先,ActivityThread从main()函数中开始执行,调用prepareMainLooper()为UI线程创建一个消息队列(MessageQueue). 然后创建一个ActivityThread对象,在ActivityThread的初始化代码中会创建一个H(Handler)对象和一个ApplicationThread(Binder)对象.其中Binder负责接收远程AmS的IPC调用,接收到调用后,则通过Handler把消息发送到消息队列,UI主线程会异步地从消息

从源码角度入手实现RecyclerView的Item点击事件

转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6758373.html RecyclerView 作为 ListView 和 GridView 的替代产物,相信在Android界已广为流传. RecyclerView 本是不会有类似 ListView 的那种点击事件,但是知道和会用又是两种情况,跟随我一起从源码角度分析,RecyclerView 点击事件. 首先看一下 Google 对 ListView 家族谱的介绍: 可以看出 ListView 归根

从源码角度深入理解Toast

Toast这个东西我们在开发中经常用到,使用也很简单,一行代码就能搞定: 1: Toast.makeText(this, "333", Toast.LENGTH_LONG).show(); 但是我们经常会遇到这样一种情况,比如说我们有两个按钮,每次点击之后就会弹出一个Toast,但是如果这两个按钮快速点击,我们看到的效果是这样的:   但实际上我们想要的效果应该是这样的:   源码解读 看了上面两张效果图的对比之后,我们应该明白了,第一种情况是在我们点击完之后,Toast还在不断的显示

Snackbar新版Toast 从源码角度完全解析

我们将会从一下几个角度了解Snackbar. 1.什么是Snackbar 2.如何使用Snackbar 3.有哪些常用的API 4.从源码角度分析其实现 5.总结 1.什么是Snackbar Snackbar是一个轻量级的用户操作反馈工具,类似于Toast,但是比Toast更美观和实用,当你操作它的时候,Snackbar会置顶显示一个一定时间的简要的信息在屏幕的底部,并且它还可以添加事件. 2.如何使用Snackbar Snackbar .make(parentLayout, R.string.

从源码角度分析linearLayout测量过程以及weight机制

???上文从源码角度分析了view和viewGroup的measure机制,如果还没有阅读的同志们,可以前往从源码角度分析Android View的绘制机制(一)阅读.下面我再结合linearLayout的measure过程解释以下两个问题的缘由. 问题一: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent