我一直在安卓SDK平台上工作,现在还不清楚如何保存应用程序的状态。因此,考虑到“你好,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分钟,没有发现任何明显的东西。


当前回答

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

用户终止应用程序并在稍后重新打开,但应用程序需要重新加载上次会话中的数据,这需要使用持久存储方法,例如使用SQLite。用户切换应用程序,然后返回到原始状态,并希望恢复到他们停止的状态-在onSaveInstanceState()和onRestoreInstanceState(()中保存和恢复捆绑数据(如应用程序状态数据)通常就足够了。

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

这并不是说一种方法比另一种更好,就像所有事情一样,重要的是了解你需要什么样的行为并选择最合适的方法。

其他回答

我的同事写了一篇文章,解释了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();
}

虽然公认的答案是正确的,但有一种更快、更容易的方法可以使用名为Icepick的库在Android上保存“活动”状态。Icepick是一个注释处理器,它负责为您保存和恢复状态时使用的所有样板代码。

对Icepick:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

  @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);
  }
}

与执行以下操作相同:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

Icepick可以处理任何使用Bundle保存其状态的对象。

您需要重写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");
}

或者从碎片中提取。

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(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");
}

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

使用Android ViewModel和SavedStateHandle持久化可序列化数据

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        binding.setViewModel(new ViewModelProvider(this).get(ViewModel.class));
        binding.setLifecycleOwner(this);
        setContentView(binding.getRoot());
    }

    public static class ViewModel extends AndroidViewModel {

        //This field SURVIVE the background process reclaim/killing & the configuration change
        public final SavedStateHandle savedStateHandle;

        //This field NOT SURVIVE the background process reclaim/killing but SURVIVE the configuration change
        public final MutableLiveData<String> inputText2 = new MutableLiveData<>();


        public ViewModel(@NonNull Application application, SavedStateHandle savedStateHandle) {
            super(application);
            this.savedStateHandle = savedStateHandle;
        }
    }
}

在布局文件中

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewModel"
            type="com.xxx.viewmodelsavedstatetest.MainActivity.ViewModel" />
    </data>

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">


        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:autofillHints=""
            android:hint="This field SURVIVE the background process reclaim/killing &amp; the configuration change"
            android:text='@={(String)viewModel.savedStateHandle.getLiveData("activity_main/inputText", "")}' />

        <SeekBar
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress='@={(Integer)viewModel.savedStateHandle.getLiveData("activity_main/progress", 50)}' />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="This field SURVIVE the background process reclaim/killing &amp; the configuration change"
            android:text='@={(String)viewModel.savedStateHandle.getLiveData("activity_main/inputText", "")}' />

        <SeekBar
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress='@={(Integer)viewModel.savedStateHandle.getLiveData("activity_main/progress", 50)}' />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="This field NOT SURVIVE the background process reclaim/killing but SURVIVE the configuration change"
            android:text='@={viewModel.inputText2}' />

    </LinearLayout>
</layout>

测试:

1. start the test activity
2. press home key to go home
3. adb shell kill <the test activity process>
4. open recent app list and restart the test activity

有一种方法可以让Android在不执行任何方法的情况下保存状态。只需将此行添加到“活动”声明中的清单:

android:configChanges="orientation|screenSize"

应该是这样的:

<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>

在这里您可以找到有关此属性的更多信息。

建议让Android为您处理,而不是手动处理。