面试题:android中A Activity 打开B Activity,为什么A Activity的onStop()方法最后被调用

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题:android中A Activity 打开B Activity,为什么A Activity的onStop()方法最后被调用相关的知识,希望对你有一定的参考价值。

如下是一段典型的Activity间切换的日志,从A Activity切换到B Activity:

10-17 20:54:42.247: I/com.example.servicetest.AActivity(5817): onCreate() 1166919192 taskID=66  
10-17 20:54:42.263: I/com.example.servicetest.AActivity(5817): onStart() 1166919192 taskID=66  
10-17 20:54:42.263: I/com.example.servicetest.AActivity(5817): onResume() 1166919192 taskID=66  
10-17 20:54:46.997: I/com.example.servicetest.AActivity(5817): onPause() 1166919192 taskID=66  
10-17 20:54:47.021: I/com.example.servicetest.BActivity(5817): onCreate() 1166971824 taskID=66  
10-17 20:54:47.028: I/com.example.servicetest.BActivity(5817): onStart() 1166971824 taskID=66  
10-17 20:54:47.028: I/com.example.servicetest.BActivity(5817): onResume() 1166971824 taskID=66  
10-17 20:54:47.099: I/com.example.servicetest.AActivity(5817): onStop() 1166919192 taskID=66 

当触发从AActivity切换到BActivity时的日志如下:

10-17 20:54:46.997: I/com.example.servicetest. AActivity(5817): onPause() 1166919192 taskID=66
10-17 20:54:47.021: I/com.example.servicetest.BActivity(5817): onCreate() 1166971824 taskID=66
10-17 20:54:47.028: I/com.example.servicetest.BActivity(5817): onStart() 1166971824 taskID=66
10-17 20:54:47.028: I/com.example.servicetest.BActivity(5817): onResume() 1166971824 taskID=66
10-17 20:54:47.099: I/com.example.servicetest. AActivity(5817): onStop() 1166919192 taskID=66

先AActivity的onPause()被调用,然后是BActivity的初始化流程(onCreate() --> onStart() --> onResume()),再然后是AActivity的onStop()被调用。

问题来了,为什么不是先AActivity的onPause()、onStop()被调用,然后再BActivity的初始化流程(onCreate() --> onStart() --> onResume())?

或者又为什么不是先BActivity的初始化流程(onCreate() --> onStart() --> onResume()),再AActivity的onPause()、onStop()被调用?

如果所有的初始化都在onCreate()中实现,会有什么问题?

首先,Activity的onCreate()被调用时,Activity还不可见,如果要做一些动画,既然视图还不存在,在onCreate中来启动动画,明显有问题;

其次,AActivity 切换到 BActivity,再切换到 AActivity(我们假定是AActivity的同一个实例),由于实例已经存在,所以onCreate不会再被调用,那AActivity从后台切换至前台时,有可能需要一些初始化,那就没法再被调用到了,也有问题;

如果所有的初始化都在onStart()中实现,会有什么问题?

首先,onCreate()注释中,是明确建议 setContentView()、findViewById() 要在 onCreate() 中被调用,但我实测了一下,在onStart()中调用 setContentView()、findViewById() 功能也是正常的;

其次,onStart() 被调用时,Activity可能是可见了,但还不是可交互的,onResume()的注释中都明确地说了这不是Activity对用户是可见的最好的指示器,onStart() 在这之前被调用,那有一些特殊的初始化相关的逻辑在这里被调用也会有问题。

如果把所有的去初始化都在onStop()中实现,会有什么问题?

1、 在 onResume() 的注释中,建议是在onResume()中打开独占设备(比如相机),与onResume()对应的是onPause(),所以所有的去初始化操作放在onStop()中执行,可能会引出新的问题;

2、onStop() 的注释中明确地写了,在内存不足而导致系统无法保留此进程的情况下,onStop() 可能都不会被执行

Activity间跳转时,为什么是先AActivity的onPause()被调用,然后是BActivity的初始化流程(onCreate() --> onStart() --> onResume()),再然后是AActivity的onStop()被调用?

1、在 onResume() 的注释中,建议是在onResume()中打开独占设备(比如相机),与onResume()对应的是onPause(),关闭相机的操作也应该在此方法中被调用;否则,考虑一下如下场景:

如果AActivity打开了相机,我们点击某按钮要跳转到BActivity中,BActivity也想打开相机;假设AActivity的onPause() 在 BActivity启动后再被调用,那BActivity根本就无法再正常启动相机。

2、onPause() 的注释中,也明确地说了,在这个方法中执行停止动画等比较耗CPU的操作,如果不先执行这些操作,就先启动新应用,然后再来执行此操作,确实是不合逻辑;

当用户触发某事件切换到新的Activity,用户肯定是想尽快进入新的视图进行操作,上面已经说了,在onResume()一般会打开独占设备,开启动画等,当需要从AActivity切换到BActivity时,先执行AActivity中的与onResume()相对应的onPause()操作,比如关闭独占设备,关闭动画,或其它耗费cpu的操作;以防止BActivity也需要使用这些资源,关闭耗CPU的操作,也有利于BActivity运行的流畅。

底层执行AActivity的onPause()时,有一定的时间限制的,当ActivityManagerService通知应用进程暂停指定的Activity时,如果对应的onPause()在500ms内还没有执行完,ActivityManagerService就会强制关闭这个Activity。如下就是对应的onPause()执行超时常量定义:

// How long we wait until giving up on the last activity to pause.  This  
// is short because it directly impacts the responsiveness of starting the  
// next activity.  
static final int PAUSE_TIMEOUT = 500;  // 定义在ActivityStack.java中  

AActivity中比较消耗资源的部分关闭后,再切换到BActivity中执行BActivity的初始化,显示BActivity中的View。

当BActivity已经执行显示出来了,用户可以交互,后台再去执行AActivity的onStop()操作,即使这里面有些比较耗时的操作,也没有关系,这是在后台执行所以也不影响用户的体验。

开发中可能会遇到个别问题,如发现onStop的调用时机受下一个页面的影响,本页面的onStop是在下一个页面onResume,onWindowFocusChanged等之后才会调用,如果在onResume和onWindowFocusChanged中进行了耗时的操作,会导致前一个页面的onStop不能被回调。这个细节会被大部分人忽略,但有时会触发意想不到的bug。

上面说了这么多,其实还没有解释为什么AActivity的onStop()方法会在BActivity的OnResume方法之后执行,下面我们需要借助源码来找下原因。

Activity启动源码分析-- onStop,onDestroy探寻

1.目的

这个是这个启动的最后一篇,讲Activity生命周期里面回调的最后几个方法。前面已经讲到了onCreate,onStart,onResume,onPause。还差onStop,onDestroy,onRestart。看一下Activity启动过程中,他们究竟藏在哪里。

这篇结束后,Activity的几个主要生命周期就都介绍了。当然,读者也可以继续拓展其他方法。

onStop还能算在这个启动过程中,onDestroy,onRestart其实就没多大关系了。下面会分开讲。

2.OnStop

还是先放图。

接着上篇,

ActivityThread

        public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) 
        ...
        //onResume执行完后,会在空闲时执行Idler
        Looper.myQueue().addIdleHandler(new Idler());
    

IdleHandler是MessageQueue类里的一个借口,messages为空的时候就会执行IdleHandler。看下Idler是怎么实现queueIdle()的。

ActivityThread

    private class Idler implements MessageQueue.IdleHandler 
        @Override
        public final boolean queueIdle() 
            ...
                mNewActivities = null;
                //这就相当眼熟了,我们直接往AMS去找
                IActivityManager am = ActivityManager.getService();
                ActivityClientRecord prev;
                do 
                    if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    if (a.activity != null && !a.activity.mFinished) 
                        try 
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                            a.createdConfig = null;
                         catch (RemoteException ex) 
                            throw ex.rethrowFromSystemServer();
                        
                    
                    prev = a;
                    a = a.nextIdle;
                    prev.nextIdle = null;
                 while (a != null);
            
            ...
            return false;
        
    

ActivityManagerService

    @Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) 
        final long origId = Binder.clearCallingIdentity();
        synchronized (this) 
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) 
                ActivityRecord r =
                        mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
                                false /* processPausingActivities */, config);
                if (stopProfiling) 
                    if ((mProfileProc == r.app) && mProfilerInfo != null) 
                        clearProfilerLocked();
                    
                
            
        
        Binder.restoreCallingIdentity(origId);
    

ActivityStackSupervisor

    @GuardedBy("mService")
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) 
        ...

        //找到当前不在界面上为Pause状态的Activity
        // Atomically retrieve all of the other things to do.
        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
                true /* remove */, processPausingActivities);
        NS = stops != null ? stops.size() : 0;
        if ((NF = mFinishingActivities.size()) > 0) 
            finishes = new ArrayList<>(mFinishingActivities);
            mFinishingActivities.clear();
        

        if (mStartingUsers.size() > 0) 
            startingUsers = new ArrayList<>(mStartingUsers);
            mStartingUsers.clear();
        

        // Stop any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NS; i++) 
            r = stops.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) 
                //Pause的Activitiy正在finish状态,这里自然不是
                if (r.finishing) 
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
                            "activityIdleInternalLocked");
                 else 
                    //走你
                    stack.stopActivityLocked(r);
                
            
        

        //onDestory会在这调用正在fininshing的Activity,是mFinishingActivities维护的。但是在前面没有看到往mFinishingActivities添加成员的地方
        // Finish any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NF; i++) 
            r = finishes.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) 
                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
            
        
        ...
    

    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
            boolean remove, boolean processPausingActivities) 
        ArrayList<ActivityRecord> stops = null;

        final boolean nowVisible = allResumedActivitiesVisible();
        for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) 
            ...
            if (remove) 
                final ActivityStack stack = s.getStack();
                final boolean shouldSleepOrShutDown = stack != null
                        ? stack.shouldSleepOrShutDownActivities()
                        : mService.isSleepingOrShuttingDownLocked();
                //非显示状态
                if (!waitingVisible || shouldSleepOrShutDown) 
                    //处于Pause
                    if (!processPausingActivities && s.isState(PAUSING)) 
                        // Defer processing pausing activities in this iteration and reschedule
                        // a delayed idle to reprocess it again
                        removeTimeoutsForActivityLocked(idleActivity);
                        scheduleIdleTimeoutLocked(idleActivity);
                        continue;
                    

                    if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
                    if (stops == null) 
                        stops = new ArrayList<>();
                    
                    //添加返回
                    stops.add(s);
                    mStoppingActivities.remove(activityNdx);
                
            
        

        return stops;
    

ActivityStack

    private boolean adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) 
        final ActivityStack stack =
                mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
        final String myReason = reason + " adjustFocusToNextFocusableStack";
        if (stack == null) 
            return false;
        

        final ActivityRecord top = stack.topRunningActivityLocked();

        if (stack.isActivityTypeHome() && (top == null || !top.visible)) 
            // If we will be focusing on the home stack next and its current top activity isn't
            // visible, then use the move the home stack task to top to make the activity visible.
            return mStackSupervisor.moveHomeStackTaskToTop(reason);
        

        stack.moveToFront(myReason);
        return true;
    

    final void stopActivityLocked(ActivityRecord r) 
        ...
                //再眼熟不过,让ClientHandler去执行StopActivityItem了。
                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
        ...
    

这里从LifecycleManagerApp传递的过程前面已描述两次,这里不再赘述。

StopActivityItem

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) 
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    

ActivityThread

    @Override
    public void handleStopActivity(IBinder token, boolean show, int configChanges,
            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) 
        ...
        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
                reason);

        ...
    

    private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
            boolean saveState, boolean finalStateRequest, String reason) 
        ...
            if (!keepShown) 
                callActivityOnStop(r, saveState, reason);
            
        ...
    

    private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) 
        // Before P onSaveInstanceState was called before onStop, starting with P it's
        // called after. Before Honeycomb state was always saved before onPause.
        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
        final boolean isPreP = r.isPreP();
        if (shouldSaveState && isPreP) 
            callActivityOnSaveInstanceState(r);
        

        try 
            //这里可以看到Stop方法的调用了
            r.activity.performStop(false /*preserveWindow*/, reason);
         catch (SuperNotCalledException e) 
            throw e;
         catch (Exception e) 
            if (!mInstrumentation.onException(r.activity, e)) 
                throw new RuntimeException(
                        "Unable to stop activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
            
        
        r.setState(ON_STOP);

        if (shouldSaveState && !isPreP) 
            callActivityOnSaveInstanceState(r);
        
    

可以从上面看到,onStop实在MessageQueue空闲才会调用。不像onPuase,onResume一定会被调用。

3.OnDestroy

从之前见到的方法里,唯一好像与onDestroy相关的。是ActivityStackSupervisor.mFinishingActivities。但是启动流程里没有见到正常赋值的地方啊,所以全局搜索一下,猜测一下是否有DestroyActivityItem这个类,果然是有的。再往回推调用链,可以看到正常调用有找到两处,这里单列一下System_server端的调用关系:

对应App手动调用finish方法:
->ActivityManagerService:finishActivity
->ActivityStack:requestFinishActivityLocked
->ActivityStack:finishActivityLocked
->ActivityStack:finishCurrentActivityLocked
->ActivityStack:destroyActivityLocked

还有一个是

调用处可以参考第三篇 Activity启动源码分析(3)-- 新app进程创建过程,在ActivityThread.attach有一个GcWatcher,内存占用大于3/4就会触发
->ActivityManagerService:releaseSomeActivities
->ActivityStackSupervisor:releaseSomeActivitiesLocked
->ActivityStack:releaseSomeActivitiesLocked
->ActivityStack:destroyActivityLocked

这篇的话,有看过Pause过程的话,是相当容易理解的。因为这里调用方式都一致。不会有上一篇app进程创建过程设计过程那么复杂。当然,我的文章只能描述其中一部分,还有大部分需要读者再从相关文章拓展。

其他地方看着都是异常时调用,还有系统强杀。所以onDestroy和onCreate一定会成对调用吗?并不是的,onDestroy并不一定会被调用。正常的流程下,只有在调用了finish后3/4内存占用后触发GC才调用了。所以反注册一定需要很是小心。

推荐的一种写法是

    @Override
    protected void onStop() 
        super.onStop();
        if(isFinishing())
            //unRegister
        
    

3.OnRestart

在上一篇,我们有把cycleToPath具体分析。其实在这个里面

TransactionExecutorHelper

    public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) 
        ...
        //如果是从大的生命周期往小的生命周期变化,如onStop到onResume
        else  // finish < start, can't just cycle down
            if (start == ON_PAUSE && finish == ON_RESUME) 
                // Special case when we can just directly go to resumed state.
                mLifecycleSequence.add(ON_RESUME);
             else if (start <= ON_STOP && finish >= ON_START) 
                // Restart and go to required state.

                //这里看到,最大也就是到onStop,并没有调用onDestroy方法
                // Go to stopped state first.
                for (int i = start + 1; i <= ON_STOP; i++) 
                    mLifecycleSequence.add(i);
                
                //然后调用了Restart
                // Restart
                mLifecycleSequence.add(ON_RESTART);
                // Go to required state
                for (int i = ON_START; i <= finish; i++) 
                    mLifecycleSequence.add(i);
                
             else 
                // Relaunch and go to required state

                // Go to destroyed state first.
                for (int i = start + 1; i <= ON_DESTROY; i++) 
                    mLifecycleSequence.add(i);
                
                // Go to required state
                for (int i = ON_CREATE; i <= finish; i++) 
                    mLifecycleSequence.add(i);
                
            
        

        //移除了onResume
        // Remove last transition in case we want to perform it with some specific params.
        if (excludeLastState && mLifecycleSequence.size() != 0) 
            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
        

        return mLifecycleSequence;
    

一言蔽之,就是onDestroy没有调用的时候,Activity的状态由大变小,就会走onRestart。


上面提到onStop实在MessageQueue空闲才会调用,这个MessageQueue就是onResume的时候添加的Idler。

Looper.myQueue().addIdleHandler(new Idler());

IdleHandler 详解

IdleHandler 是 MessageQueue 内定义的一个接口,一般可用于做性能优化。当消息队列内没有需要立即执行的 message 时,会主动触发 IdleHandler 的 queueIdle 方法。返回值为 false,即只会执行一次;返回值为 true,即每次当消息队列内没有需要立即执行的消息时,都会触发该方法。

public final class MessageQueue 
    public static interface IdleHandler 
        boolean queueIdle();
    

使用方式

通过获取 looper 对应的 MessageQueue 队列注册监听。

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() 
    @Override
    public boolean queueIdle() 
        // doSomething()
        return false;
    
);

源码解析

IdleHandler 的执行源码很短。

Message next() 
    // 隐藏无关代码...
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (; ; ) 
        // 隐藏无关代码...
        // If first time idle, then get the number of idlers to run.
        // Idle handles only run if the queue is empty or if the first message
        // in the queue (possibly a barrier) is due to be handled in the future.
        if (pendingIdleHandlerCount < 0
                && (mMessages == null || now < mMessages.when)) 
            pendingIdleHandlerCount = mIdleHandlers.size();
        
        if (pendingIdleHandlerCount <= 0) 
            // No idle handlers to run.  Loop and wait some more.
            mBlocked = true;
            continue;
        
        if (mPendingIdleHandlers == null) 
            mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
        
        mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    
    // Run the idle handlers.
    // We only ever reach this code block during the first iteration.
    for (int i = 0; i < pendingIdleHandlerCount; i++) 
        final IdleHandler idler = mPendingIdleHandlers[i];
        mPendingIdleHandlers[i] = null; // release the reference to the handler
        boolean keep = false;
        try 
            keep = idler.queueIdle();
         catch (Throwable t) 
            Log.wtf(TAG, "IdleHandler threw exception", t);
        
        if (!keep) 
            synchronized (this) 
                mIdleHandlers.remove(idler);
            
        
    
    // Reset the idle handler count to 0 so we do not run them again.
    pendingIdleHandlerCount = 0;
    // While calling an idle handler, a new message could have been delivered
    // so go back and look again for a pending message without waiting.
    nextPollTimeoutMillis = 0;
  1. 在 MessageQueue 里 next 方法的 for 死循环内,获取 mIdleHandlers 的数量 pendingIdleHandlerCount;

  1. 通过 mMessages == null || now < mMessages.when 判断当前消息队列为空或者目前没有需要执行的消息时,给 pendingIdleHandlerCount 赋值;

  1. 当数量大于 0,遍历取出数组内的 IdleHandler,执行 queueIdle() ;

  1. 返回值为 false 时,主动移除监听 mIdleHandlers.remove(idler);

使用场景

  1. 如果启动的 Activity、Fragment、Dialog 内含有大量数据和视图的加载,导致首次打开时动画切换卡顿或者一瞬间白屏,可将部分加载逻辑放到 queueIdle() 内处理。例如引导图的加载和弹窗提示等;

  1. 系统源码中 ActivityThread 的 GcIdler,在某些场景等待消息队列暂时空闲时会尝试执行 GC 操作;

  1. 系统源码中 ActivityThread 的 Idler,在 handleResumeActivity() 方法内会注册 Idler(),等待 handleResumeActivity 后视图绘制完成,消息队列暂时空闲时再调用 AMS 的 activityIdle 方法,检查页面的生命周期状态,触发 activity 的 stop 生命周期等。
    这也是为什么我们 BActivity 跳转 CActivity 时,BActivity 生命周期的 onStop() 会在 CActivity 的 onResume() 后

  1. 一些第三方框架 Glide 和 LeakCanary 等也使用到 IdleHandler,感兴趣的朋友可以看看源码;

以上是关于面试题:android中A Activity 打开B Activity,为什么A Activity的onStop()方法最后被调用的主要内容,如果未能解决你的问题,请参考以下文章

Android常见 面试题

Android基础面试题

Android 面试题:为什么 Activity 都重建了 ViewModel 还存在?—— Jetpack 系列

Android 面试题总结之Android 基础

第3章 Android基础相关面试题

android 2018 面试题