题 使用“保存实例状态”保存Android活动状态


我一直在研究Android SDK平台,有点不清楚如何保存应用程序的状态。因此,考虑到'Hello,Android'示例的这种小型重新设计:

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

我认为这对于最简单的情况就足够了,但无论我如何离开应用程序,它总是会响应第一条消息。

我确信解决方案就像覆盖一样简单 onPause 或类似的东西,但我已经在文档中捅了大约30分钟左右,并没有找到任何明显的东西。


2238
2017-09-30 04:41


起源


什么时候savedInstanceState == null,什么时候不为null? - Trojan.ZBOT
正如你所说,你正在明确地摧毁你的活动,远离它,例如通过向后推。实际上,使用此“savedInstanceState”的场景是Android破坏您的娱乐活动。对于intance:如果在活动运行时更改手机的语言(因此需要加载项目中的不同资源)。另一种非常常见的情况是将手机旋转到一侧,以便重新创建活动并以横向显示。 - villoren
要获取第二条消息,请在开发选项中启用“不要保留活动”。按主页按钮,从最近返回。 - Yaroslav Mytkalyk
这非常有帮助 developer.android.com/training/basics/activity-lifecycle/... - Syed Raza Mehdi
你可以用:onSaveInstanceState(Bundle savedInstanceState) - HPbyP


答案:


你需要覆盖 onSaveInstanceState(Bundle savedInstanceState) 并将要更改的应用程序状态值写入 Bundle 像这样的参数:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

Bundle本质上是一种存储NVP(“名称 - 值对”)映射的方式,它将被传入 onCreate() 并且 onRestoreInstanceState() 你在哪里提取这样的值:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

您通常会使用此技术来存储应用程序的实例值(选择,未保存的文本等)。


2275
2017-09-30 06:12



这有可能在手机上运行,​​但不能在模拟器中运行吗?我似乎无法获得非null的savedInstanceState。 - Adam Jack
我有一个点列表,如何保存此数组列表中的所有点,然后还原它们? - AZ_
小心:你需要在将值添加到Bundle之前调用super.onSaveInstanceState(savedInstanceState),否则它们将在该调用中消失(Droid X And​​roid 2.2)。 - jkschneider
小心:官方文档指出,你应该在onPause-Method中保存重要信息,因为o​​nsaveinstance-method不是android生命周期的一部分。 developer.android.com/reference/android/app/Activity.html - schlingel
这个事实有效地使 onSaveInstanceState 几乎无用,只是因为屏幕方向改变的情况。在几乎所有其他情况下,您永远不能依赖它,并且需要在其他地方手动保存您的UI状态。或者通过覆盖BACK按钮行为来防止您的应用被杀死。我不明白他们为什么甚至首先这样实现它。完全不直观。除了这个非常特殊的方法之外,你不能拥有系统允许你保存的Bundle。 - chakrit


savedInstanceState 仅用于保存与当前活动实例关联的状态,例如当前导航或选择信息,以便在Android销毁并重新创建活动时,它可以像之前一样返回。请参阅文档 onCreate 和 onSaveInstanceState

对于更长寿的状态,请考虑使用SQLite数据库,文件或首选项。看到 保存持久状态


375
2017-09-30 05:03



什么时候savedInstanceState == null,什么时候不为null? - Trojan.ZBOT
当系统正在创建Activity的新实例时,savedInstanceState为null,而在还原时不为null。 - Gabriel Câmara
......提出了这个问题 什么时候 系统是否需要创建一个新的Activity实例。退出应用程序的某些方法不会创建包,因此必须创建新实例。这是根本问题;这意味着一个人不能 依靠 关于bundle的存在,必须做一些替代的持久存储方式。 onSave / onRestoreInstanceState的好处是它是系统可以做的一种机制 突然,不消耗太多系统资源。所以支持它是好的,并且具有持久存储以便从app更优雅地退出。 - ToolmakerSteve


请注意它是  安全使用 onSaveInstanceState 和 onRestoreInstanceState  对于持久数据,根据活动状态的文件 http://developer.android.com/reference/android/app/Activity.html

该文件陈述(在“活动生命周期”部分):

请注意,保存很重要   持久数据 onPause() 代替   的 onSaveInstanceState(Bundle)   因为后者不属于   生命周期回调,所以不会   在所描述的每种情况下都会召唤   在其文件中。

换句话说,将持久数据的保存/恢复代码放入 onPause() 和 onResume()

编辑:有关进一步说明,请点击此处 onSaveInstanceState() 文档:

在激活某个活动之前调用此方法,以便在此时执行   在未来的某个时候,它可以恢复其状态。对于   例如,如果活动B在活动A前面,在某些活动中发布   点活动A被杀死以回收资源,活动A将有   有机会通过它保存其用户界面的当前状态   方法使用户返回活动A时的状态   用户界面可以通过恢复 onCreate(Bundle) 要么    onRestoreInstanceState(Bundle)


366
2018-05-25 23:22



只是为了挑剔:这也不是不安全的。这只取决于你想保留什么以及多久,@伯纳德在他原来的问题中并不完全清楚。 InstanceState非常适合保留当前的UI状态(输入到控件中的数据,列表中的当前位置等),而Pause / Resume是长期持久存储的唯一可能性。 - Pontus Gagge
这应该被低估了。使用(保存|恢复)InstanceState(如生命周期方法)是不安全的(即在其中执行除保存/恢复状态之外的任何其他操作)。它们非常适合保存/恢复状态。另外,您想如何在onPause和onResume中保存/恢复状态?您没有在可以使用的那些方法中获得Bundles,因此您必须在数据库,文件等中使用其他一些状态保存,这是愚蠢的。 - Felix
我们不应该对这个人投票,至少他努力通过文档,我认为我们的人在这里实际上建立一个知识渊博的社区,并互相帮助,而不是DOWN VOTE。所以1投票支持这项工作,我会要求你们不要投票,而不是投票或不投票......这个人清楚了解人们在阅读文档时会产生的困惑。 1投票:) - AZ_
我认为这个答案不值得投票。至少他努力回答并引用了doco的一个部分。 - GSree
这个答案是绝对正确的,值得UP投票,而不是下来!让我澄清那些没有看到它的人之间的状态差异。 GUI状态(如选定的单选按钮和输入字段中的某些文本)比数据状态重要得多,例如添加到ListView中显示的列表中的记录。后者必须在onPause中存储到数据库,因为它是唯一保证的调用。如果你把它放在onSaveInstanceState中,那么如果没有调用那么你就有丢失数据的风险。但是如果出于同样的原因没有保存单选按钮选择 - 这不是什么大问题。 - JBM


我的同事写了一篇文章解释Android设备上的应用程序状态,包括对活动生命周期和状态信息的解释,如何存储状态信息以及保存到状态 Bundle 和 SharedPreferences 和 看看这里

本文介绍了三种方法:

使用Instance State Bundle存储应用程序生命周期(即临时)的本地varible / UI控制数据

[Code sample – Store State in State Bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState) 
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

使用共享首选项在应用程序实例之间(即永久地)存储本地变量/ UI控制数据

[Code sample – Store State in SharedPreferences]
@Override
protected void onPause() 
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store    
  // Commit to storage
  editor.commit();
}

使用保留的非配置实例,在应用程序生命周期内的活动之间保持对象实例在内存中存活

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass;// Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance() 
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

171
2017-08-27 13:54



@ MartinBelcher-Eigo文章谈到SharedPreferences中的数据“此数据被写入设备上的数据库......”我相信数据存储在文件系统的应用程序目录中的文件中。 - Tom
@Tom SharefPrefs数据被写入xml文件。 xml是一种数据库吗?我会说是;) - MaciejGórski
注意 editor.apply() 比...更快 editor.commit()。 - Fred
链接已经死了。 - Sakiboy


这是Android开发的经典“问题”。这里有两个问题:

  • 有一个微妙的Android Framework错误,它使开发过程中的应用程序堆栈管理变得非常复杂,至少在旧版本上是这样(不完全确定是否/何时/如何修复)。我将在下面讨论这个错误。
  • 管理此问题的“正常”或预期方式本身相当复杂,具有onPause / onResume和onSaveInstanceState / onRestoreInstanceState的二元性

浏览所有这些线程,我怀疑开发人员大多数时间同时讨论这两个不同的问题......因此所有混淆和报告“这对我不起作用”。

首先,澄清“预期”行为:onSaveInstance和onRestoreInstance是脆弱的,仅适用于瞬态。预期用途(afaict)用于在电话旋转(方向改变)时处理活动娱乐。换句话说,预期的用法是当您的Activity仍然在逻辑上“在顶部”时,但仍然必须由系统重新实例化。保存的Bundle不会在进程/ memory / gc之外保留,因此如果您的活动进入后台,则无法真正依赖它。是的,也许你的Activity的记忆将在它的背景之旅中存活并逃脱GC,但这不可靠(也不可预测)。

因此,如果您的某个场景中存在有意义的“用户进度”或应该在应用程序的“启动”之间保留的状态,则指导是使用onPause和onResume。您必须自己选择并准备持久性商店。

但是 - 有一个非常混乱的错误使所有这一切变得复杂。细节在这里:

http://code.google.com/p/android/issues/detail?id=2373

http://code.google.com/p/android/issues/detail?id=5277

基本上,如果您的应用程序是使用SingleTask标志启动的,然后您从主屏幕或启动器菜单启动它,那么后续调用将创建一个新任务...您将有效地拥有两个不同的应用实例居住在同一堆栈中......这非常快速地变得非常奇怪。当您在开发期间(即从Eclipse或Intellij)启动应用程序时,这似乎会发生,因此开发人员会遇到这种情况。但也通过一些应用程序商店更新机制(因此它也会影响您的用户)。

在我意识到我的主要问题是这个错误,而不是预期的框架行为之前,我在这些线程中奋战了几个小时。一个伟大的写作和 解决方法 (更新:见下文)似乎来自用户@kaciula在这个答案中:

主页按键行为

2013年6月更新几个月后,我终于找到了'正确'的解决方案。您不需要自己管理任何有状态的startupApp标记,您可以从框架中检测到这一点并适当地保释。我在LauncherActivity.onCreate的开头附近使用它:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

126
2017-10-19 23:47



为什么Android再次成为第二选择的移动操作系统? 埋头在手中 - Nick Bauer


onSaveInstanceState当系统需要内存并杀死应用程序时调用。用户刚刚关闭应用程序时不会调用它。所以我认为应用程序状态也应该保​​存在 onPause 它应该保存到某些持久存储器中 Preferences 要么 Sqlite


70
2018-05-07 00:21



对不起,这不太正确。在需要重新生成活动之前调用onSaveInstanceState。即每次用户旋转设备。它用于存储瞬态视图状态。当android强制关闭应用程序时,实际上没有调用onSaveInstanceState(这就是为什么它对于存储重要的应用程序数据是不安全的)。但是,保证在活动被杀死之前调用onPause,因此它应该用于在首选项或Squlite中存储永久信息。正确答案,错误的理由。 - moveaway00
@ moveaway00 - 部分不正确。如果系统杀死应用程序以恢复资源,它 不 调用onSaveInstanceState。从 developer.android.com/training/basics/activity-lifecycle/...  - “系统也可能破坏您的活动......前台活动需要更多资源,因此系统必须关闭后台进程才能恢复内存....如果系统因此而破坏活动 系统约束 ...系统记住它存在...保存的数据......是存储在Bundle对象中的键值对的集合。“ - ToolmakerSteve


这两种方法都是有用且有效的,并且最适合不同的场景:

  1. 用户终止应用程序并在以后重新打开它,但应用程序需要从上一个会话重新加载数据 - 这需要持久的存储方法,例如使用SQLite。
  2. 用户切换应用程序,然后返回到原始状态,并希望从中断处继续 - 保存和恢复捆绑数据(例如应用程序状态数据) onSaveInstanceState() 和 onRestoreInstanceState() 通常是足够的。

如果以持久方式保存状态数据,则可以将其重新加载 onResume() 要么 onCreate() (或实际上在任何生命周期调用上)。这可能是也可能不是期望的行为。如果将它存储在一个包中 InstanceState,它是瞬态的,仅适用于存储用于同一用户'会话'的数据(我使用松散的术语会话),但不适用于'会话'之间。

并不是说一种方法比另一种方法更好,就像所有方法一样,了解您需要的行为并选择最合适的方法非常重要。


59
2018-06-27 16:17



在寻找持久存储时,我们如何在SQLite和Preferences之间进行选择? - Deco


就我而言,拯救国家充其量只是一个障碍。如果您需要保存持久数据,只需使用 SQLite的 数据库。 Android制作它 SOOO 简单。

像这样的东西:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close()
    {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType)
    {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue)
    {
        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

之后是一个简单的电话

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

50
2018-06-23 17:07



因为加载SQLite数据库需要很长时间,所以考虑到这是向用户显示应用程序UI的关键路径。我实际上没有计时,所以我很高兴得到纠正,但是加载和打开数据库文件肯定不会很快? - Tom
非常感谢您提供解决方案,新手可以剪切并粘贴到他们的应用程序中并立即使用! @Tom就速度而言,存储1000对需要大约7秒钟,但你可以在AsyncTask中完成。但是,您需要添加finally {cursor.close()},否则在执行此操作时它将从内存泄漏中崩溃。 - Noumenon
我遇到了这个,虽然它看起来很整洁,但我还是犹豫是否想在谷歌眼镜上使用它,这是我最近正在研究的设备。 - Stephen Tetreault


我想我找到了答案。让我用简单的话说出我的所作所为:

假设我有两个活动,activity1和activity2,我从activity1导航到activity2(我已在activity2中完成了一些工作),再次通过单击activity1中的按钮返回到活动1。现在在这个阶段我想回到activity2,我想在上次离开activity2时看到我的activity2处于相同的状态。

对于上面的场景,我所做的是在清单中我做了一些像这样的更改:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

在按钮点击事件的activity1中我做了这样的事情:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

在按钮点击事件的activity2中我做了这样的事情:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

现在将会发生的事情是,我们在activity2中所做的任何更改都不会丢失,我们可以在与之前离开的状态相同的状态下查看activity2。

我相信这是答案,这对我来说很好。如果我错了,请纠正我。


50
2018-02-05 11:35



@bagusflyer关心更具体???您的评论没有帮助,任何人都无法在此基础上为您提供帮助。 - Stephen Tetreault
这是对不同情况的回答:同一应用程序中的两个活动。 OP是关于 离开 应用程序(例如主页按钮或其他方式切换到其他应用程序)。 - ToolmakerSteve


onSaveInstanceState() 用于瞬态数据(恢复为 onCreate()/onRestoreInstanceState()onPause() 持久数据(恢复) onResume())。 来自Android技术资源:

的onSaveInstanceState() 如果Activity被停止并且可能在恢复之前被杀死,则由Android调用!这意味着它应该存储重新启动Activity时重新初始化为相同条件所需的任何状态。它与onCreate()方法相对应,实际上传入onCreate()的savedInstanceState Bundle与onSaveInstanceState()方法中构造为outState的Bundle相同。

的onPause() 和 的onResume() 也是免费的方法。当Activity结束时总是调用onPause(),即使我们发起了这个(例如使用finish()调用)。我们将使用它将当前注释保存回数据库。好的做法是释放在onPause()期间可以释放的任何资源,以便在处于被动状态时占用更少的资源。


36
2018-01-17 18:28





重新创建活动

在某些情况下,您的活动会因应用程序的正常行为而被销毁,例如当用户按下“返回”按钮或您的活动通过调用发出自己的销毁信号时 finish()。如果系统当前已停止且未长时间使用或前台活动需要更多资源,系统也可能会破坏您的活动,因此系统必须关闭后台进程才能恢复内存。

当你的 activity 被销毁,因为用户按下后退或 activity 完成自己,系统的概念 Activity 实例永远消失,因为行为表明不再需要活动。但是,如果系统因系统约束(而不是正常的应用程序行为)而破坏活动,那么虽然实际的Activity实例已经消失,但系统会记住它存在,如果用户导航回到它,系统会创建一个新的使用一组描述活动状态的已保存数据的活动实例 destroyed。系统用于恢复先前状态的已保存数据称为“实例状态”,是存储在Bundle对象中的键值对的集合。

要保存有关活动状态的其他数据,必须覆盖onSaveInstanceState()回调方法。系统在用户离开您的活动时调用此方法,并向其传递Bundle对象,该对象将在您的活动意外销毁时保存。如果系统必须稍后重新创建活动实例,它会将相同的Bundle对象传递给它们 onRestoreInstanceState() 和 onCreate() 方法。 enter image description here

当系统开始停止您的活动时,它会调用 onSaveInstanceState() (1)因此,您可以指定要保存的其他状态数据,以防必须重新创建Activity实例。如果活动被销毁并且必须重新创建相同的实例,则系统会将(1)中定义的状态数据传递给两者 onCreate() 方法(2)和 onRestoreInstanceState() 方法(3)。

保存你的 Activity 州

当您的活动开始停止时,系统会调用 onSaveInstanceState() 所以你的活动可以用一组键值对来保存状态信息。此方法的默认实现保存有关活动视图层次结构状态的信息,例如文本中的文本 EditText 小部件或者滚动位置 ListView

要保存活动的其他状态信息,您必须实施 onSaveInstanceState() 并将键值对添加到Bundle对象。例如:

  static final String STATE_SCORE = "playerScore";
  static final String STATE_LEVEL = "playerLevel";

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
  // Save the user's current game state
  savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
  savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

  // Always call the superclass so it can save the view hierarchy state
  super.onSaveInstanceState(savedInstanceState);
}

注意:始终调用超类实现 onSaveInstanceState()所以默认实现可以保存视图层次结构的状态。

恢复你的 Activity 州

在先前销毁活动之后重新创建活动时,您可以从系统传递活动的Bundle中恢复已保存的状态。这俩 onCreate() 和 onRestoreInstanceState() 回调方法收到相同的 Bundle 包含实例状态信息。

因为 onCreate() 无论系统是创建活动的新实例还是重新创建前一个实例,都要调用方法,在尝试读取之前,必须检查状态Bundle是否为null。如果它为null,则系统正在创建活动的新实例,而不是恢复已销毁的先前实例。

例如,以下是如何恢复某些状态数据的方法 onCreate()

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); // Always call the superclass first

 // Check whether we're recreating a previously destroyed instance
 if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
 } else {
    // Probably initialize members with default values for a new instance
 }

 }

而不是恢复国家 onCreate() 你可以选择实施 onRestoreInstanceState(),系统调用之后 onStart() 方法。系统调用 onRestoreInstanceState() 仅当存在要恢复的已保存状态时,您无需检查Bundle是否为null:

  public void onRestoreInstanceState(Bundle savedInstanceState) {
  // Always call the superclass so it can restore the view hierarchy
  super.onRestoreInstanceState(savedInstanceState);

  // Restore state members from saved instance
  mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
  mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

36
2018-04-15 04:13