Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/225.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何使用保存实例状态保存活动状态?_Android_Android Activity_Application State - Fatal编程技术网

Android 如何使用保存实例状态保存活动状态?

Android 如何使用保存实例状态保存活动状态?,android,android-activity,application-state,Android,Android Activity,Application State,我一直在安卓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 m

我一直在安卓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分钟左右,没有发现任何明显的问题。
savedInstanceState
仅用于保存与活动的当前实例关联的状态,例如,当前导航或选择信息,因此,如果Android销毁并重新创建活动,它可以恢复到以前的状态。有关和,请参阅文档


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

您需要覆盖
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.
}
@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");
}
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) {
        }
    }
}
<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);
Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);
<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>
捆绑包本质上是一种存储NVP(“名称-值对”)映射的方法,它将被传递到
onCreate()
onRestoreInstanceState()
中,然后从活动中提取值,如下所示:

@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.
}
@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");
}
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) {
        }
    }
}
<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);
Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);
<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>
或者从一个碎片

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

您通常会使用此技术存储应用程序的实例值(选择、未保存的文本等)。当系统需要内存并终止应用程序时,将调用

onSaveInstanceState
。当用户刚刚关闭应用程序时,不会调用它。因此,我认为应用程序状态也应该保存在
onPause
中,它应该保存到一些持久性存储中,比如
Preferences
Sqlite
当活动转到后台时,确实调用了
onSaveInstanceState()

引用文件: “在活动可能被终止之前调用此方法,以便在将来某个时间返回时可以恢复其状态。”

请注意,根据中有关活动状态的文档,对持久数据使用
onSaveInstanceState
onRestoreInstanceState
是不安全的

文档说明(在“活动生命周期”部分):

请注意,保存是很重要的 改为在
onPause()中保存数据
onSaveInstanceState(捆绑包)
因为后者不属于 生命周期回调,因此不会发生 在所描述的每种情况下调用 在其文件中

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

编辑:为了进一步澄清,这里是
onSaveInstanceState()
文档:

在终止活动之前调用此方法,以便在 在将来的某个时间返回时,它可以恢复其状态。对于 例如,如果活动B在活动A之前启动,并且 点活动A被终止以回收资源,活动A将 有机会通过此选项保存其用户界面的当前状态 方法,以便当用户返回到活动A时 用户界面可以通过
onCreate(Bundle)
onRestoreInstanceState(捆绑包)


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

本条涵盖三种方法:

使用实例状态包存储应用程序生存期(即临时)的局部变量/UI控制数据 使用共享首选项在应用程序实例之间(即永久)存储本地变量/UI控制数据 使用保留的非配置实例在应用程序生命周期内的活动之间保持对象实例在内存中的活动状态
就我而言,拯救国家充其量只是一个难题。如果需要保存持久数据,只需使用数据库即可。安卓让它变得很简单

大概是这样的:

@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.
}
@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");
}
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) {
        }
    }
}
<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);
Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);
<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>
之后再打个简单的电话

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

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

  • 用户终止应用程序并在以后重新打开它,但应用程序需要从上一个会话重新加载数据–这需要一种持久存储方法,例如使用SQLite
  • 用户切换应用程序,然后返回到原始应用程序,并希望从中断的地方恢复—在
    onSaveInstanceState()
    onRestoreInstanceState()
    中保存和恢复捆绑包数据(如应用程序状态数据)通常就足够了
  • 如果以持久方式保存状态数据,则可以在
    onResume()
    onCreate()
    中重新加载状态数据(或在任何生命周期调用中重新加载)。这可能是也可能不是期望的行为。如果将其存储在捆绑包中的
    InstanceState
    ,则它是暂时的,仅适用于存储数据以供在同一用户“会话”(我松散地使用术语“会话”)中使用,而不是在“会话”之间使用

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

    onSaveInstanceState()
    对于瞬态数据(在
    onCreate()
    /
    onRestoreInstanceState()
    )和
    onPause()中还原)
    用于持久数据(在
    onResume()中还原)。
    来自Android技术资源:

    onSaveInstanceState()由Android调用,如果活动正在停止并且可能会被终止
    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);
      }
    }
    
    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
    }
    
       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
       @Override
        protected void onSaveInstanceState(Bundle outState) {
          outState.putString("key","Welcome Back")
            super.onSaveInstanceState(outState);       //save state
        }
    
       @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");
        }
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState.apply {
            putInt("intKey", 1)
            putString("stringKey", "String Value")
            putParcelable("parcelableKey", parcelableObject)
        })
    }
    
        val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
        val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
        val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable
    
    @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);
    }
    
     onCreate(Bundle savedInstanceState)
    
    onRestoreInstanceState(Bundle savedInstanceState)
    
    @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);
    }
    
    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
    }
    
    android:configChanges="orientation|screenSize"
    
    <activity
        android:name=".activities.MyActivity"
        android:configChanges="orientation|screenSize">
    </activity>
    
    public class HelloAndroidViewModel extends ViewModel {
       public Booelan firstInit = false;
    
        public HelloAndroidViewModel() {
            firstInit = false;
        }
        ...
    }
    
    public class HelloAndroid extends Activity {
    
      private TextView mTextView = null;
      HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
      /** Called when the activity is first created. */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        mTextView = new TextView(this);
    
        //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
        if(!viewModel.firstInit){
            viewModel.firstInit = true
            mTextView.setText("Welcome to HelloAndroid!");
        }else{
           mTextView.setText("Welcome back.");
        }
    
        setContentView(mTextView);
      }
    }
    
    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