Android onSaveInstanceState和onRestoreInstanceState调用时机、及大小限制

对于 Activity 的 onSaveInstanceState 方法大家都不会陌生,当 Activity 在不正常销毁的情况下,就会调用 onSaveInstanceState 方法,并将 Activity 中需要保存的数据(比如 View 状态 或者我们自己的数据)保存到这个方法的参数 Bundle 中。

一、onSaveInstanceState(Bundle outState)的调用时机

onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。

Activity的onSaveInstanceState回调时机,取决于app的targetSdkVersion

targetSdkVersion低于11的app,onSaveInstanceState方法会在Activity.onPause之前回调;

targetSdkVersion低于28的app,则会在onStop之前回调;

28之后,onSaveInstanceStateonStop回调之后才回调。

演示项目的targetSDK大于28,所以全部都是在onStop之后调用的:

//************************调用 onSaveInstanceState() ***************************************
/**
 * 按home键:
 * 2021-01-29 16:30:29.493 27040-27040/com.example.saveinstancetest D/buder: onCreate
 * 2021-01-29 16:30:29.494 27040-27040/com.example.saveinstancetest D/buder: onStart
 * 2021-01-29 16:30:29.496 27040-27040/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:30:35.313 27040-27040/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:30:35.340 27040-27040/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:30:35.343 27040-27040/com.example.saveinstancetest E/buder: onSaveInstanceState
 *
 */

/**
 * 从显示onResume时,进入到最近任务:
 * 2021-01-29 16:31:30.103 27040-27040/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:31:43.253 27040-27040/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:31:43.274 27040-27040/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:31:43.275 27040-27040/com.example.saveinstancetest E/buder: onSaveInstanceState
 */

/**
 * 从显示onResume时,按电源键:
 * 2021-01-29 16:33:29.434 27040-27040/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:33:33.045 27040-27040/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:33:33.049 27040-27040/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:33:33.050 27040-27040/com.example.saveinstancetest E/buder: onSaveInstanceState
 */

/**
 * 从本activity进入到另外一个activity时:
 * 2021-01-29 16:35:12.927 27040-27040/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:35:15.492 27040-27040/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:35:16.054 27040-27040/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:35:16.055 27040-27040/com.example.saveinstancetest E/buder: onSaveInstanceState
 */

/**
 * 屏幕方向切换时:
 * 2021-01-29 16:53:48.422 11050-11050/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:53:50.186 11050-11050/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:53:50.187 11050-11050/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:53:50.188 11050-11050/com.example.saveinstancetest E/buder: onSaveInstanceState
 * 2021-01-29 16:53:50.189 11050-11050/com.example.saveinstancetest D/buder: onDestroy
 * 2021-01-29 16:53:50.236 11050-11050/com.example.saveinstancetest D/buder: onCreate
 * 2021-01-29 16:53:50.237 11050-11050/com.example.saveinstancetest E/buder: onRestoreInstanceState方式一:恢复数据,onCreate的Restore,改变后的数据 change data
 * 2021-01-29 16:53:50.242 11050-11050/com.example.saveinstancetest D/buder: onStart
 * 2021-01-29 16:53:50.243 11050-11050/com.example.saveinstancetest E/buder: onRestoreInstanceState方式二:数据恢复 onRestore,改变后的数据 change data
 * 2021-01-29 16:53:50.245 11050-11050/com.example.saveinstancetest D/buder: onResume
 */

二、onRestoreInstanceState什么时机被调用

onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。

onRestoreInstanceState(Bundle outState)会在以下情况被调用:
1、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)
2、由于内存紧张导致后台运行的程序被kill掉时(这种不太好模拟)

//************************调用 onRestoreInstanceState() ***************************************

/**
 * 屏幕方向切换时:
 * 2021-01-29 16:53:48.422 11050-11050/com.example.saveinstancetest D/buder: onResume
 * 2021-01-29 16:53:50.186 11050-11050/com.example.saveinstancetest D/buder: onPause
 * 2021-01-29 16:53:50.187 11050-11050/com.example.saveinstancetest D/buder: onStop
 * 2021-01-29 16:53:50.188 11050-11050/com.example.saveinstancetest E/buder: onSaveInstanceState
 * 2021-01-29 16:53:50.189 11050-11050/com.example.saveinstancetest D/buder: onDestroy
 * 2021-01-29 16:53:50.236 11050-11050/com.example.saveinstancetest D/buder: onCreate
 * 2021-01-29 16:53:50.237 11050-11050/com.example.saveinstancetest E/buder: onRestoreInstanceState方式一:恢复数据,onCreate的Restore,改变后的数据 change data
 * 2021-01-29 16:53:50.242 11050-11050/com.example.saveinstancetest D/buder: onStart
 * 2021-01-29 16:53:50.243 11050-11050/com.example.saveinstancetest E/buder: onRestoreInstanceState方式二:数据恢复 onRestore,改变后的数据 change data
 * 2021-01-29 16:53:50.245 11050-11050/com.example.saveinstancetest D/buder: onResume
 */

模拟代码:

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "buder";
    private static final String STORE_KEY = "key_one";
    TextView mTextView;
    Button mBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");
        mTextView = findViewById(R.id.text);
        mBtn = findViewById(R.id.btn);
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), MainActivity2.class);
                startActivity(intent);
            }
        });

        //方式一:数据恢复,从K-V中获取销毁之前的存储值
        if (savedInstanceState != null) {
            String test = savedInstanceState.getString(STORE_KEY);
            Log.e(TAG, "方式一:恢复数据,onCreate的Restore," + test);
            mTextView.setText(test);
        }
    }

    //在pause/stop之间进行界面上的数据存储,到K-V中
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e(TAG, "onSaveInstanceState");
//        String change = mTextView.getText().toString();
        outState.putString(STORE_KEY, "改变后的数据 change data");
    }

    //方式二:数据恢复,从K-V中获取销毁之前的存储值
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String test = savedInstanceState.getString(STORE_KEY);
        Log.e(TAG, "方式二:数据恢复 onRestore," + test);
        mTextView.setText(test);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

项目完整代码 

三、onCreate()里也有Bundle参数,可以用来恢复数据,它和onRestoreInstanceState有什么区别?

因为onSaveInstanceState 不一定会被调用,所以onCreate()里的Bundle参数可能为空,如果使用onCreate()来恢复数据,一定要做非空判断。

而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用。

四、onSaveInstanceState 的数据存在哪里?为什么限制了大小?

在实际使用的时候你可能会发现当保存的数据过大的时候就会看到如下的 log 日志

javabinder !!! FAILED BINDER TRANSACTION !!!

甚至可能发生异常(在高版本下会抛出异常,低版本直接打印日志)

android.os.TransactionTooLargeException

上面的信息都是表示 Bundle 传输的数据过大,那么问题来了, onSaveInstanceState 中 Bundle 的数据是存放在哪里,为什么又限制?

1.官网

在官网 有关于 Parcelables and Bundles 的一段介绍,其中就有提到 Bundles 的数据大小问题。

image.png

翻译一下: Binder 传输缓冲区是一个限制的大小的区域,大小为 1MB,这块缓冲区用于所有进程间的通信,也就是 Binder 通信。这些传输包括 onSaveInstanceState , startActivity 和其他与系统的交互,当传输的数据超过这个大小的时候就会抛出异常。

特别是 onSaveInstanceState 方法,因其需要在 Activity 返回的时候提供数据,官网建议是数据大小不大于 50K.

关于 startActivity 和其他系统交互需要使用 Binder 进行跨进程通信我们知道,但是你可能就有疑问 onSaveInstanceState 不是在自己进程中做 Activity 某些状态的保存,为什么需要 Binder 呢?

关于 Binder 机制中 Binder 缓冲区的限制,网上有很多文章,这里就不进行说明了。

2.系统源码追踪

ActivityThread

在追 onSaveInstanceState 的方法源码的时候,想要找到关于 Binder 方面的内容,就顺着Activity 的方法追,很多人都是到 Application 的 接口中就迷路了.

public interface ActivityLifecycleCallbacks {
       void onActivityCreated(Activity activity, Bundle savedInstanceState);
       void onActivityStarted(Activity activity);
       void onActivityResumed(Activity activity);
       void onActivityPaused(Activity activity);
       void onActivityStopped(Activity activity);
       void onActivitySaveInstanceState(Activity activity, Bundle outState);
       void onActivityDestroyed(Activity activity);
   }

其实这是一个错误的方向,其实只要想到 onSaveInstanceState 有点类似于 Activity 中其他生命周期的方法,就可以发现 onSaveInstanceState 最开始也是有 ActivityThread 做统一管理的,那么 onSaveInstanceState 的调用 也就和 ActivityThread 有关。

Bundle 的创建

在 ActivityThread 的源码中可以看到有这么个方法

private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

其中最后调用了 mInstrumentation.callActivityOnSaveInstanceState
那么就到 mInstrumentation 类中查找

public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
            PersistableBundle outPersistentState) {
        activity.performSaveInstanceState(outState, outPersistentState);
    }

这个方法中又调用了 activity.performSaveInstanceState 的方法

final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
      onSaveInstanceState(outState, outPersistentState);
      saveManagedDialogs(outState);
      storeHasCurrentPermissionRequest(outState);
      if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
              ", " + outPersistentState);
  }
  
  ...
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
      onSaveInstanceState(outState);
  }

最后调用的是 Activity onSaveInstanceState 方法 ,显然 callCallActivityOnSaveInstanceState 的 r.state 就是 onSaveInstanceState 的方法的中的参数 Bundle .而 r 就是 ActivityThread 的内部类 ActivityClientRecord

static final class ActivityClientRecord {
        IBinder token;
        int ident;
        Intent intent;
        String referrer;
        IVoiceInteractor voiceInteractor;
        Bundle state; // 这个就是 r.state 也就是 onSaveInstanceState 的 Bundle 
    
}

到这里我们知道 Bundle 是怎样创建的,但是关于 Binder 传输的问题,这里也没有体现,那么究竟是在哪里涉及的 Binder 传输的呢?

onSaveInstanceState 调用时机

这里先补充一个点就是 onSaveInstanceState 的方法的调用时机

* <p>If called, this method will occur before {@link #onStop}.  There are
   * no guarantees about whether it will occur before or after {@link #onPause}.
   *
   * @param outState Bundle in which to place your saved state.
   *
   * @see #onCreate
   * @see #onRestoreInstanceState
   * @see #onPause
   */
  protected void onSaveInstanceState(Bundle outState) {
      outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

      outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
      Parcelable p = mFragments.saveAllState();
      if (p != null) {
          outState.putParcelable(FRAGMENTS_TAG, p);
      }
      if (mAutoFillResetNeeded) {
          outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
          getAutofillManager().onSaveInstanceState(outState);
      }
      getApplication().dispatchActivitySaveInstanceState(this, outState);
  }

从注释中可以看到这个方法的调用会在 onStop 前,那么我们就去 ActivityTread 中看看再 onStop 前发生了什么。

private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {

        ActivityClientRecord r = mActivities.get(token);
        ....
        StopInfo info = new StopInfo();
        //
        performStopActivityInner(r, info, show, true, "handleStopActivity");

        ...
        info.activity = r;
        //注意这里 将 r.state 赋值给  info.state
        info.state = r.state;
        info.persistentState = r.persistentState;
        
        //然后调用 Handler 的 post 方法将这个 StopInfo 类传递给消息队列 
        mH.post(info);
        mSomeActivitiesChanged = true;
    }

StopInfo 是一个实现了 Runnable 接口的类,且内部也有Bundle 的引用 所以可以 将 ActivityClientRecord 的 Bundle 赋值给 StopInfo 的 Bundle

private static class StopInfo implements Runnable {
        ActivityClientRecord activity;
        Bundle state;
         ....
            
        
    }

最后因为 StopInfo 被 Handle post 之后,那么就会执行其相应的 run 方法。

private static class StopInfo implements Runnable {
        ActivityClientRecord activity;
        Bundle state;
        PersistableBundle persistentState;
        CharSequence description;

        @Override public void run() {
            // Tell activity manager we have been stopped.
            try {
                //注意到这里将 state 作为参数调用了 ActivityManager.getService() 方法。
                ActivityManager.getService().activityStopped(
                    activity.token, state, persistentState, description);
            } catch (RemoteException ex) {
               
                if (ex instanceof TransactionTooLargeException
                        && activity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                    return;
                }
                throw ex.rethrowFromSystemServer();
            }
        }
    }

到这里就可以明白了 onSaveInstanceState 中保存的 Bundle 信息是存在内存中的,且因为是涉及到 Activity 的状态的保存,就需要交由 ActivityManager 进程去做一个管理,所以就需要 Binder 传输做一个跨进程的通信将 Bundle 的数据传递给 ActivityManager。因此 onSaveInstanceState 也涉及到了 Binder 传输,自然而然就受到 Binder 缓冲区大小的限制,到这里问题就解决了。

猜你喜欢

转载自blog.csdn.net/cpcpcp123/article/details/113396301