我一直在工作上SDK平台,这是有点不清楚如何保存应用程序的的状态。所以,鉴于这一微小的重新工具的'Hello,安卓'的例子:

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分钟左右,并没有发现任何显而易见的。

有帮助吗?

解决方案

您需要覆盖 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");
}

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

其他提示

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

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

请注意 NOT 安全使用 onSaveInstanceState onRestoreInstanceState 用于持久数据,根据 http://developer.android中有关活动状态的文档。 COM /参考/机器人/应用/ Activity.html

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

  

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

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

编辑:有关进一步说明,请参阅 onSaveInstanceState()文档:

  

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

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

本文介绍了三种方法:

使用实例状态包

存储应用程序生命周期(即临时)的局部变量/ 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();
}

这是一个典型的'问题'的安卓发展。有两个问题:

  • 有一种微妙的安卓框架的错误,大大复杂化应堆管理在开发期间,至少在传统版本(不能完全肯定,如果/当/如何它是固定的)。我将讨论这个问题如下。
  • '正常'的或预期的方式来管理这个问题本身,而不是复杂的双重性onPause/onResume和onSaveInstanceState/onRestoreInstanceState

浏览在所有这些线我怀疑,很多的时间开发人员都在谈论这两个不同的问题,同时...因此,所有的混乱和报告的"这并不能工作对我来说"。

第一,澄清'意在'的行为:onSaveInstance和onRestoreInstance是脆弱的,只用于过渡状态。预期使用(afaict)是处理活动,娱乐时的电话转动(方向变化)。换句话说,旨在使用的是当你的活动仍然逻辑上',但仍然必须重新实例化,使其通过系统。保存束是不保留以外的处理/记忆/gc的,所以你不能真正依赖这一点,如果你的活去的背景。是的,也许你的活动的存储器中的生存将其旅行的背景和逃GC的,但这是不可靠的(它也不是可预测的).

所以如果你有一个方案,其中有的是有意义'的用户的进度'或状态,应该保留之间的'启动'的应用程序,指导是使用onPause和onResume.你必须选择和准备的持久性存储自己。

但是,有一个非常混乱的错误而变得复杂,所有这一切。详细信息:

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

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

基本上,如果应用程序启动与为singletask标志,再后来上启动它的自主屏幕上或启动菜单,然后,随后的调用将创建一个新的任务...你将有效地有两个不同的实例应用程序居住在同一堆...它得到非常奇怪的速度非常快。这似乎发生时启动程序期间的发展(即从食或用浏览器),因此开发的运行成本很多。但也通过一些应用程序的储存更新的机制(因此它影响用户)。

我闯过这些线时间之前,我意识到,我的主要问题是这个错误,不打算框架行为。一个伟大的书面记录和 解决方法 (更新:见下文)似乎是从用户@kaciula在这个回答:

家按键行为

更新月2013:几个月后,我终于找到了"正确"的解决方案。你不需要管理任何有状态startedApp标志自己,你可以检测到这种自的框架和保适当。我使用这个接近开始我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;
    }
}

onSaveInstanceState 被称为当系统所需要的记忆和杀死一个应用程序。这是不当的用户只是关闭的应用程序。所以我觉得应用的状态下也应该存在 onPause 它应保存到一些永久性存储喜欢 PreferencesSqlite

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

  1. 用户终止应用程序并在以后重新打开它,但应用程序需要从上一个会话重新加载数据&#8211;这需要持久的存储方法,例如使用SQLite。
  2. 用户切换应用程序然后回到原始状态并想要从中断处继续 - 在 onSaveInstanceState() onRestoreInstanceState()通常就足够了。
  3. 如果以持久方式保存状态数据,可以在 onResume() onCreate()(或实际在任何生命周期调用中)重新加载。这可能是也可能不是期望的行为。如果将它存储在 InstanceState 的包中,则它是瞬态的,仅适用于存储在同一用户“会话”中使用的数据。 (我松散地使用术语会话)但不在会话之间使用#。

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

就我而言,拯救国家充其量只是一个障碍。如果您需要保存持久数据,只需使用 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;

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

假设我有两个活动,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。

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

用于瞬态数据的

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

  如果Activity被停止并且可能在恢复之前被杀死,则Android会调用

onSaveInstanceState()!这意味着它应该存储在重新启动Activity时重新初始化为相同条件所需的任何状态。它与onCreate()方法相对应,实际上传入onCreate()的savedInstanceState Bundle与你在onSaveInstanceState()方法中构造为outState的Bundle相同。

     

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

当活动转到后台时,真的 onSaveInstance 状态callen

来自文档的引用: 在将活动置于这样的背景状态之前调用方法 onSaveInstanceState(Bundle)

为了帮助减少样板,我使用以下接口来读取/写入 Bundle 以保存实例状态。


首先,创建一个用于注释实例变量的接口:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

然后,创建一个类,其中将使用反射将值保存到包中:

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

示例用法:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Icicle.load(savedInstanceState, this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Icicle.save(outState, this);
    }

}

注意:此代码改编自名为 AndroidAutowire 的图书馆项目,该项目已获得< a href =“https://raw.githubusercontent.com/CardinalNow/AndroidAutowire/master/LICENSE”> MIT许可证。

与此同时,我一般不再使用

Bundle savedInstanceState & Co

大多数活动的生命周期过于复杂且没有必要。

谷歌表示,它甚至不可靠。

我的方法是立即在首选项中保存所有更改:

 SharedPreferences p;
 p.edit().put(..).commit()

在某些方面,SharedPreferences的工作方式与Bundles类似。 当然,首先必须从偏好中读取这些值。

对于复杂数据,您可以使用SQLite而不是使用首选项。

应用此概念时,活动将继续使用上次保存的状态,无论是初始打开还是重新启动,或者由于后端堆栈而重新打开。

回答原来的问题。savedInstancestate是空的,因为活动是永远不会被重新创建的。

你的活动,只会是重新创建一个国家束时:

  • 结构改变,例如改变方向,或者手机的语言而可能需要一个新的活动实例。
  • 你回来的应用程序从背后的操作系统已经销毁的活动。

安卓将破坏活动的背景下存储器中的压力或之后,他们一直在背景中的一段延长的时间。

测试时你的世界你好例子有几种方法可以离开和返回活动。

  • 当你按下按钮回活动的完成。重新启动该程序是一个全新的实例。你是不是恢复的背景。
  • 当你按按钮或使用的任务的切换台的活动将进入的背景。航行时回的应用程序onCreate将只能被称为如果该活动不得不被破坏。

在大多数情况下如果你只是紧迫的家庭,然后开展的应用程序,再次活动将不需要重新创建的。它已经存在的记忆,所以onCreate()不会被称为。

有一个选项下设置-开发商选择所谓的"不要让活动"。当启用新评价将始终是破坏活动和重建他们时,他们背景.这是一个很好的选择离开时启用的发展,因为它模拟的最坏的情况。(低存储器设备回收活动所有的时间)。

其他的答案是宝贵的,他们教你正确的方式来存储状态,但我不觉得他们真的回答,为什么你的代码不是工作方式预期。

onSaveInstanceState(bundle) onRestoreInstanceState(bundle)方法仅在旋转屏幕(方向更改)时对数据持久性很有用。
在应用程序之间切换时它们甚至都不好(因为 onSaveInstanceState()方法被调用,但 onCreate(bundle) onRestoreInstanceState(bundle)是没有再次调用。
对于更多持久性,请使用共享首选项阅读本文

我的问题是我只在应用程序生命周期内需要持久性(即单个执行包括在同一个应用程序中启动其他子活动并旋转设备等)。我尝试了上述答案的各种组合,但在所有情况下都没有得到我想要的东西。最后,对我有用的是在onCreate:

期间获取对savedInstanceState的引用
mySavedInstanceState=savedInstanceState;

并在我需要时使用它来获取变量的内容,如下所示:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

我如上所述使用 onSaveInstanceState onRestoreInstanceState ,但我想我也可以或者使用我的方法在变量时保存变量(例如使用 putBoolean

创建活动时,会调用onCreate()方法。

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

savedInstanceState是Bundle类的一个对象,它第一次为null,但在重新创建时包含值。要保存Activity的状态,您必须覆盖onSaveInstanceState()。

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

将您的值放在“outState”中捆绑对象,如outState.putString(&quot; key&quot;,“Welcome Back”),并通过调用super来保存。 当活动被销毁时,它的状态被保存在Bundle对象中,并且可以在onCreate()或onRestoreInstanceState()中重新创建后恢复。在onCreate()和onRestoreInstanceState()中收到的Bundle是相同的。

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }

实现此更改基本上有两种方法。

  1. 使用 onSaveInstanceState() onRestoreInstanceState()
  2. 在清单 android:configChanges =&quot; orientation | screenSize&quot;
  3. 我真的不建议使用第二种方法。因为在我的一次经验中,它导致设备屏幕的一半在从纵向旋转到横向时变黑,反之亦然。

    使用上面提到的第一种方法,我们可以在方向更改或任何配置更改发生时保留数据。 我知道一种方法,你可以在savedInstance状态对象中存储任何类型的数据。

    示例:如果要保留Json对象,请考虑一个案例。 使用getter和setter创建一个模型类。

    class MyModel extends Serializable{
    JSONObject obj;
    
    setJsonObject(JsonObject obj)
    {
    this.obj=obj;
    }
    
    JSONObject getJsonObject()
    return this.obj;
    } 
    }
    

    现在,在onCreate和onSaveInstanceState方法的活动中,执行以下操作。它看起来像这样:

    @override
    onCreate(Bundle savedInstaceState){
    MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
    JSONObject obj=data.getJsonObject();
    //Here you have retained JSONObject and can use.
    }
    
    
    @Override
    protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //Obj is some json object 
    MyModel dataToSave= new MyModel();
    dataToSave.setJsonObject(obj);
    oustate.putSerializable("yourkey",dataToSave); 
    
    }
    

以下是来自史蒂夫·莫斯利的答案(由 ToolmakerSteve )发表的评论,这些评论将事情置于透视中(在整个onSaveInstanceState vs onPause,east cost vs west cost saga )

  

@VVK - 我部分不同意。某些退出应用程序的方法不会触发   onSaveInstanceState(oSIS)。这限制了oSIS的有用性。它的   值得支持,最小的操作系统资源,但如果一个应用程序想要   无论应用程序如何,都将用户返回到他们所处的状态   退出时,有必要使用持久存储方法。   我使用onCreate检查包,如果缺少,请检查   持久存储。这使决策集中化。我可以   从崩溃或后退按钮退出或自定义菜单项退出恢复,或   多天后回到屏幕用户。 &#8211; ToolmakerSteve Sep   19 '15 at 10:38

Kotlin代码:

保存:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

然后在 onCreate() onRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

如果您不想拥有Optionals

,请添加默认值

要获取存储在 onCreate()中的活动状态数据,首先必须通过覆盖 SaveInstanceState(Bundle savedInstanceState)方法将数据保存在savedInstanceState中。

当活动销毁 SaveInstanceState(Bundle savedInstanceState)方法被调用时,您将保存要保存的数据。当活动重启时,你在 onCreate()中得到相同的结果。(savedInstanceState不会为null,因为你在活动被销毁之前已经保存了一些数据)

简单快速解决此问题的方法是使用 IcePick

首先,在 app / build.gradle

中设置库
repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

现在,让我们查看下面的示例如何在Activity

中保存状态
public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

适用于活动,碎片或任何需要在Bundle上序列化其状态的对象(例如迫击炮的ViewPresenters)

Icepick还可以为自定义视图生成实例状态代码:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}

不确定我的解决方案是否不赞成,但我使用绑定服务来保持ViewModel状态。是将它存储在服务的内存中还是持久存储并从SQLite数据库中检索它取决于您的要求。这就是任何风格的服务,它们提供诸如维护应用程序状态和抽象通用业务逻辑之类的服务。

由于移动设备固有的内存和处理限制,我以类似于网页的方式处理Android视图。页面不维护状态,它纯粹是一个表示层组件,其唯一目的是呈现应用程序状态并接受用户输入。 Web应用程序体系结构的最新趋势采用了古老的模型,视图,控制器(MVC)模式,其中页面是视图,域数据是模型,控制器位于Web服务后面。在Android中可以使用相同的模式,View是...,View,模型是您的域数据,Controller是作为Android绑定服务实现的。每当您希望视图与控制器交互时,在开始/恢复时绑定它并在停止/暂停时取消绑定。

这种方法为您提供了强制执行Separation of Concern设计原则的额外好处,因为您可以将所有应用程序业务逻辑移动到您的服务中,从而减少多个视图中的重复逻辑,并允许视图执行另一个重要的设计原则,单一责任。

现在Android提供 ViewModels 以保存状态,你应该试试使用它而不是saveInstanceState。

什么来保存和什么不可以?

有没有想过为什么文字 EditText 得到自动保存,虽然一个方向改变?好了,这回答是给你的。

当一个实例,一项活动被摧毁,该系统重新创建一个新的实例(例如,结构变化)。它试图重新使用设置保存的数据的老活动国家(实例状态).

实例的国家是一个集合 key-value 对储存在 Bundle 对象。

通过默认系统保存的查的对象束的例子。

  • 文本 EditText
  • 滚动位置 ListView, 等等。

如果你需要的另一个变量可以保存一部分作为实例的状态,你应该 重写 onSavedInstanceState(Bundle savedinstaneState) 法。

例如, int currentScore 在GameActivity

更详细的有关onSavedInstanceState(束savedinstaneState),同时节省的数据

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

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

这么错误,如果你要忘了打电话 super.onSaveInstanceState(savedInstanceState);默认的行为 不会的工作,即文EditText不会保存。

其中选择用于恢复活动的国家?

 onCreate(Bundle savedInstanceState)

onRestoreInstanceState(Bundle savedInstanceState)

这两种方法获得同样的束的对象,因此,它并不真正的问题在哪里你写你恢复的逻辑。唯一的区别是,中 onCreate(Bundle savedInstanceState) 方法你会得到一个空检查,而不需要在后一种情况。其他的答案已经代码段。你可以参考。

更详细的有关onRestoreInstanceState(束savedinstaneState)

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

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

总是呼叫 super.onRestoreInstanceState(savedInstanceState); 因此,系统的恢复查层次通过默认

奖金

onSaveInstanceState(Bundle savedInstanceState) 援引该系统只有当用户打算回来的活动。例如,你正在使用的应用程序X突然间,你得到一个电话。你移动到来电的应用程序,然后回来的应用程序X.在这种情况下 onSaveInstanceState(Bundle savedInstanceState) 方法将被援引。

但考虑到这一点,如果一个用户按下按钮。它是假设使用者并不打算回来的活动,因此在这种情况下 onSaveInstanceState(Bundle savedInstanceState) 不会调用的系统。重点是你应该考虑所有的方案,同时节省的数据。

相关链接:

演示默认的行为
安卓正式文件.

科特林

您必须覆盖 onSaveInstanceState onRestoreInstanceState 来存储和检索您想要持久的变量

生命周期图

存储变量

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

检索变量

public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}

我有一个更好的主意。这样可以更好地保存数据而无需再次调用onCreate。当方向发生变化时,您可以从活动中禁用它。

在你的清单中:

<activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">

修改 由于内存不足,当android杀死进程时,此解决方案将无法运行。如果你需要确保数据,你需要在这种情况下使用SavedInstanceState,否则我建议使用这种方式。很酷且易于使用!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top