我一直在安卓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分钟,没有发现任何明显的东西。
Kotlin解决方案:对于onSaveInstanceState中的自定义类保存,您可以将类转换为JSON字符串并使用Gson转换进行恢复,对于单字符串、双精度、Int、Long值保存和恢复,如下所示。以下示例适用于片段和活动:
对于活动:
对于将数据放入saveInstanceState:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//for custom class-----
val gson = Gson()
val json = gson.toJson(your_custom_class)
outState.putString("CUSTOM_CLASS", json)
//for single value------
outState.putString("MyString", stringValue)
outState.putBoolean("MyBoolean", true)
outState.putDouble("myDouble", doubleValue)
outState.putInt("MyInt", intValue)
}
还原数据:
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
//for custom class restore
val json = savedInstanceState?.getString("CUSTOM_CLASS")
if (!json!!.isEmpty()) {
val gson = Gson()
testBundle = gson.fromJson(json, Session::class.java)
}
//for single value restore
val myBoolean: Boolean = savedInstanceState?.getBoolean("MyBoolean")
val myDouble: Double = savedInstanceState?.getDouble("myDouble")
val myInt: Int = savedInstanceState?.getInt("MyInt")
val myString: String = savedInstanceState?.getString("MyString")
}
您也可以在Activity onCreate上恢复它。
对于碎片:
对于saveInstanceState中的put类:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val gson = Gson()
val json = gson.toJson(customClass)
outState.putString("CUSTOM_CLASS", json)
}
还原数据:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//for custom class restore
if (savedInstanceState != null) {
val json = savedInstanceState.getString("CUSTOM_CLASS")
if (!json!!.isEmpty()) {
val gson = Gson()
val customClass: CustomClass = gson.fromJson(json, CustomClass::class.java)
}
}
// for single value restore
val myBoolean: Boolean = savedInstanceState.getBoolean("MyBoolean")
val myDouble: Double = savedInstanceState.getDouble("myDouble")
val myInt: Int = savedInstanceState.getInt("MyInt")
val myString: String = savedInstanceState.getString("MyString")
}
虽然公认的答案是正确的,但有一种更快、更容易的方法可以使用名为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保存其状态的对象。
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,请添加默认值
您需要重写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 & 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 & 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
解决这个问题的简单快捷方法是使用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'
}
现在,让我们看看下面的示例,如何在“活动”中保存状态
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);
}
}
它适用于Activities、Fragments或任何需要在Bundle上序列化其状态的对象(例如,迫击炮的ViewPresenter)
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
}