【问题标题】:Is onRestoreInstanceState only called when switching orientation horizontal-vertical - any other cases?仅在切换方向水平垂直时才调用 onRestoreInstanceState - 还有其他情况吗?
【发布时间】:2019-06-23 19:46:02
【问题描述】:

文档明确指出“This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState”。 StackOverflow 的几个问题concur

但是我发现这并不完全准确,并且可能会产生误导。 onRestoreInstanceState 在水平-垂直之间切换方向时调用。每当调用 onStart 时都不会调用它。

我修改了这个simple lab exercise 以打印出日志跟踪,并将 mCount 保存到包中并显示它。

onRestoreInstanceState 在您按下返回按钮返回主 Activity 时被调用也不在您按下 Home 按钮并重新启动时被调用。

即使我看到每次都调用了 onSaveInstanceState 和 onStop(从日志中),但在 onStart 之后我没有看到相应的 onRestoreInstanceState。这是出乎意料的。文档应该清楚地说明,只有当 onDestroy 被调用,然后在 onStart 之后,才会调用 onRestoreInstanceState。是否有其他情况(除了切换方向)可靠调用?

了解这些用例对于白盒测试很有用。

更新:android 文档已被澄清 - 请参阅下面的答案。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private int mCount = 0;
    private TextView mShowCount;
    public static final String EXTRA_MESSAGE =
            "com.example.hellotoast.extra.MESSAGE";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mShowCount = (TextView) findViewById(R.id.show_count);
        Log.d("MainActivity", "Hello World");
    }

    public void showToast(View view) {
        Toast toast = Toast.makeText(this, R.string.toast_message,
                Toast.LENGTH_SHORT);
        toast.show();
        Intent intent = new Intent(this, HelloCount.class);
        intent.putExtra(EXTRA_MESSAGE, mCount);
        startActivity(intent);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(EXTRA_MESSAGE, mCount);
        super.onSaveInstanceState(outState);
        Log.d("MainActivity", "onSaveInstanceState. saved mCount = " + mCount);

    }
    @Override
    public void onRestoreInstanceState(Bundle b) {
        super.onRestoreInstanceState(b);
        Log.d("MainActivity", "onRestoreInstanceState");

        if(b != null) {
            mCount = b.getInt(EXTRA_MESSAGE);
            Log.d("MainActivity", "onRestoreInstanceState mCount = "+mCount);
            mShowCount.setText(Integer.toString(mCount));
        }
    }

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

    }

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

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

    public void countUp(View view) {
        mCount++;
        if (mShowCount != null) {
            mShowCount.setText(Integer.toString(mCount));
        }
    }
}

HelloCount.java

import static com.example.hellotoast.MainActivity.EXTRA_MESSAGE;

public class HelloCount extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("HelloCount", "onCreate");
        setContentView(R.layout.activity_hello_count);
        TextView tv = findViewById(R.id.helloCount);
        int i =  getIntent().getIntExtra(EXTRA_MESSAGE,0);
        tv.setText(String.valueOf(i));
    }

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

    }

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

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

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hellotoast.MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/button_toast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/colorPrimary"
        android:onClick="showToast"
        android:text="@string/button_label_toast"
        android:textColor="@android:color/white" />


    <TextView
        android:id="@+id/show_count"

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="#FFFF00"
        android:gravity="center_vertical"
        android:text="@string/count_initial_value"
        android:textAlignment="center"
        android:textColor="@color/colorPrimary"
        android:textSize="120sp"
        android:textStyle="bold"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/button_toast"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        />

    <Button
        android:id="@+id/button_count"
        android:layout_below="@+id/show_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:background="@color/colorPrimary"
        android:onClick="countUp"
        android:text="@string/button_label_count"
        android:textColor="@android:color/white" />
</RelativeLayout>

activity_second.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="156dp"
        android:layout_marginEnd="8dp"
        android:text="Hello"
        android:textColor="#8BC34A"
        android:textColorHighlight="#00882A2A"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/helloCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.099" />

</android.support.constraint.ConstraintLayout>

【问题讨论】:

  • 嗯...你的问题是什么?您是正确的,onRestoreInstanceState() 是作为配置更改的一部分调用的,而不是针对每个 onStart() 调用。
  • @CommonsWare 上面引用的文档,不要这么说:“is called as part of a configuration change, not for every onStart() invocation”。这是误导。这也是为什么这么多人问 SO,为什么在 onStart 之后没有调用它
  • 但是,这不是问题。 Stack Overflow 用于提问。如果您的目标是在 Stack Overflow 上提供与该主题相关的另一个答案,请回答您链接到的问题,或者将您上面写的内容转换为某个问题的答案。
  • “我正试图引起人们的注意”——然后提出问题并answer your own question。 “还想知道在调用它时是否还有其他情况(除了切换方向)”——然后针对该特定问题提出问题。
  • @CommonsWare Android 团队更新了文档以澄清:This method is called between onStart() and onPostCreate(Bundle). This method is called only when recreating an activity; the method isn't invoked if onStart() is called for any other reason.developer.android.com/reference/android/app/…

标签: android


【解决方案1】:

它在方向改变期间被调用,而且当你的活动从内存中弹出而仍在后台时也会被调用。

您的案例是:

  1. 当您按下 home 键时,您的活动将暂停 (onStop)。当您回到它时,您会收到onStart 电话,无需恢复实例。

  2. 当您按回您退出您的应用程序时,您会收到 onDestroy 调用,isFinishing() 返回 true。当你“回来”时,没有什么可以恢复实例,新的活动被创建。

  3. 当您暂停活动(如第 1 点)并使其在后台保持一段时间(最好在其他应用程序周围导航以填满内存)时,系统可能会决定释放您的活动从记忆里。这将导致onSaveInstanceStateonDestroy 调用,当你回来时你会得到onCreate onRestoreInstanceState

您可以进入开发者设置并启用“不保留活动”选项,这将模拟低内存环境并强制活动在将它们置于后台后快速销毁。

【讨论】:

  • [Pawel] "2. 当您按下退出您的应用程序时,您会收到 onDestroy 调用...新活动已创建。"。 在这种情况下不正确。 从 HelloCount 按下返回,将带您返回 MainActivity,即使现在调用 onStart,onRestoreInstanceState 也不会。
  • @likejudo 我的意思是按回MainActivity,如果不清楚,抱歉。
【解决方案2】:

文档明确指出“当活动从先前保存的状态重新初始化时,在 onStart() 之后调用此方法,在 savedInstanceState 中给出”。几个 StackOverflow 问题是一致的。

但是我发现这并不完全准确,并且可能会产生误导。 onRestoreInstanceState 仅在水平-垂直之间切换方向时调用。每当调用 onStart 时都不会调用它。

文档没有说明每当调用 onStart 时都会调用 onRestoreInstanceState。正如您自己引用的那样,它清楚地表明它是在重新初始化活动的情况下。

我修改了这个简单的实验练习以打印出日志跟踪,并将 mCount 保存到包中并显示它。

当您按下返回按钮返回主 Activity 或按下 Home 按钮并重新启动时,不会调用 onRestoreInstanceState。

这两种情况都不会导致相关活动被终止,因此无需恢复状态。

即使我看到每次都调用了 onSaveInstanceState 和 onStop(从日志中),但在 onStart 之后我没有看到相应的 onRestoreInstanceState。这是意料之外的。

这完全在意料之中。 onSaveInstanceState 为您提供机会,当您的活动移到后台时保存您的状态以防它被杀死。没有办法提前知道它是否会被杀死,所以总是调用该方法,以防万一。但是,onRestoreInstanceState 并非如此。在这种情况下,系统会知道您是在重新启动相同的活动(因此不需要状态恢复,因此不调用该方法)还是您需要从状态恢复(因此调用该方法)。

文档应该明确说明只有调用了onDestroy,然后在onStart之后才会调用onRestoreInstanceState。

为什么?有什么区别?

可靠调用时是否还有其他情况(除了切换方向)?

为什么重要?您不应该关心将调用此方法的每一种情况。您应该只关心正确实施它以恢复您的活动状态。让系统在它认为必要的时候调用它,无论调用它的原因,你的应用都应该正常工作。

【讨论】:

  • “为什么?有什么不同?”我不同意。没有这个警告,人们期望它每次在 onStart 之后被调用。如果它对所有人都如此清晰明了,那么就不会有这么多问题了……“为什么这里没有调用 onRestoreInstanceState?”。
  • 我的意思是,它对您编写应用程序有什么影响?它是一个进入 Android 系统的钩子,如果它被你调用,你需要处理它。 何时发生这种情况应该是透明的并且与您无关。了解自己的学习细节可能会很好,但是 - 实际上 - 这对您如何编写应用程序没有影响。
  • 这对我测试应用程序的方式肯定有影响。例如如果 android 在我汽车的仪表板中。将汽车翻转到一侧然后再回到车轮上有点困难。
  • 如果您通过将应用加载到汽车中并翻转汽车以触发方向更改来测试保存和恢复状态,那么您遇到的问题比不完整的文档更大......
【解决方案3】:

我读到 onRestoreInstanceState 在以下情况下肯定会被调用:

  1. 水平-垂直方向变化。

  2. 语言输入变化。

  3. 更改为多窗口(无论这意味着什么)。

了解这些用例对于白盒测试很有用。

更新

https://issuetracker.google.com/issues/135968242

Android 团队有updated the docs 澄清:

此方法在 onStart() 和 onPostCreate(Bundle) 之间调用。这 仅在重新创建活动时调用方法;方法不是 如果出于任何其他原因调用 onStart() 则调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-20
    • 2017-06-30
    • 2011-10-29
    • 2017-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多