How to Save an Activity State Using the Save Instance State

How can I save an activity state using the save instance state?

You need to override onSaveInstanceState(Bundle savedInstanceState) and write the application state values you want to change to the Bundle parameter like this:

@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.
}

The Bundle is essentially a way of storing a NVP ("Name-Value Pair") map, and it will get passed in to onCreate() and also onRestoreInstanceState() where you would then extract the values from activity like this:

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

Or from a fragment.

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

You would usually use this technique to store instance values for your application (selections, unsaved text, etc.).

Save and restore instance state of an Activity manually

I finally came up with the solution described below.

A word on usage: this will keep the state of the download activity in main memory, where it will occupy space. Also, Android may kill a background app at any time, typically if it runs low on memory. If that happens, the state information will be lost.

For my particular use case I decided I could live with these limitations: the state information written by my app is in the kilobyte range, and if the state information is lost, it can be recreated (it just takes time to fetch the list from the server). YMMV.

In the onStop() method of the download activity, do:

Bundle outState = new Bundle();
this.onSaveInstanceState(outState);

Store outState in a place where it can be retrieved when needed. In my case, the BroadcastReceiver which processes the download manager events was the place to put it.

To bring up the Activity from the BroadcastReceiver, do the following:

Intent downloadIntent = new Intent(context, DownloadActivity.class);
downloadIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.putExtra("savedInstanceState", savedInstanceState);
context.startActivity(downloadIntent);

In the onCreate() method of the download activity, do:

@Override
protected void onCreate(Bundle savedInstanceState) {
Bundle state = savedInstanceState;
if (state == null)
state = this.getIntent().getBundleExtra(Const.KEY_SAVED_INSTANCE_STATE);
super.onCreate(state);
}

How to save instance state of a Layout in android

You can save your last Activity state and reuse it when recreating the activity:

int layoutId = R.layout.activity_main;

@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("layoutId", layoutId);
super.onSaveInstanceState(outState);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
layoutId = savedInstanceState.getInt("layoutId", R.layout.activity_main);
}
setContentView(layoutId);
}

public void firstQuestion(View view) {
layoutId = R.layout.first_question;
setContentView(layoutId);
}

What's the right way to save instance state only on rotation?

Turns out my question was wrong from the beginning.

When the device rotates, Android calls onSaveInstanceState(Bundle outState) and onRestoreInstanceState(Bundle savedInstanceState) as a way for the developer to save/restore state. When the activity is destroyed via back button or finish(), onSaveInstanceState is not called and the savedInstanceState bundle that's passed to onCreate() is null. So trying to answer the "was this created after a rotation or not?" question is irrelevant.

Posted in case someone has the same question and thanks to all that took time to read and help.

save Instance State in Android studio: Kotlin

Here is the full code, You didn't add many details but this is how the sample project looks, it enables to save count and then set it to TextView:

MainActivity.kt:

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

companion object {
const val COUNT_KEY = "COUNT_KEY" // const key to save/read value from bundle
}

private var count = 0 // count value with setter. It will be easier, You can change this value and don't have to think about setting TextView.text
set(value) {
field = value
txtCount.text = value.toString()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("MyTag", "onCreate")

txtCount.text = count.toString()

butIncrement.setOnClickListener {
count++
}

}

override fun onSaveInstanceState(outState: Bundle) { // Here You have to save count value
super.onSaveInstanceState(outState)
Log.i("MyTag", "onSaveInstanceState")

outState.putInt(COUNT_KEY, count)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) { // Here You have to restore count value
super.onRestoreInstanceState(savedInstanceState)
Log.i("MyTag", "onRestoreInstanceState")

count = savedInstanceState.getInt(COUNT_KEY)
}
}

activity_main.xml:

<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/butIncrement"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Increment" />

<TextView
android:id="@+id/txtCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="40sp" />

</androidx.appcompat.widget.LinearLayoutCompat>

You edited the question and added more details, here is the full Kotlin code, I tested it and it seems to work.

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

lateinit var textCount: TextView
lateinit var buttonred: Button
lateinit var buttoning: Button

companion object {
const val COUNT_KEY = "COUNT_KEY"
}

private var count = 0
set(value) {
field = value
textCount.text = value.toString()
}


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("MyTag", "onCreate")


textCount = findViewById<View>(R.id.textView) as TextView
buttonred = findViewById<View>(R.id.injury) as Button
buttoning = findViewById<View>(R.id.vial) as Button

count = 10

buttonred.setOnClickListener {
if (count >= 0) {
count--
textCount.text = count.toString()
}
}

buttoning.setOnClickListener {
if (count <= 10)
count += 3
textCount.text = count.toString()
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.i("MyTag", "onSaveInstanceState")

outState.putInt(COUNT_KEY, count)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
Log.i("MyTag", "onRestoreInstanceState")

count = savedInstanceState.getInt(COUNT_KEY)
}
}

ViewModel implementation:

To Your Gradle (module.app) file add:

dependencies {
def lifecycle_version = "2.2.0"

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

MainActivityViewModel class:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel


internal class MainActivityViewModel : ViewModel() {

private val _count: MutableLiveData<Int> = MutableLiveData()
val count: LiveData<Int>
get() = _count

init {
_count.value = START_VALUE
}

fun increment() {
_count.value = _count.value!! + 1
}

fun decrement() {
_count.value = _count.value!! - 1

}

companion object {
private const val START_VALUE = 10
}
}

MainActivity class:

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity()
{
private lateinit var viewModel: MainActivityViewModel

lateinit var textCount: TextView
lateinit var butIncrement: Button
lateinit var butDecrement: Button

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

textCount = findViewById<View>(R.id.textView) as TextView
butIncrement = findViewById<View>(R.id.injury) as Button
butDecrement = findViewById<View>(R.id.vial) as Button

viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)

viewModel.count.observe(this, {
textCount.text = it.toString()
})

butIncrement.setOnClickListener {
viewModel.increment()
}

butDecrement.setOnClickListener {
viewModel.decrement()
}
}
}

Unable to save Fragment state with onSaveInstanceState

I solved it, the problem was here

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


FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

if (savedInstanceState != null) {
dataFragment = (InputDataFragment) getSupportFragmentManager().getFragment(savedInstanceState, "dataFragment");
} else {
dataFragment = new InputDataFragment();
}

fragmentTransaction.add(R.id.container, dataFragment);
fragmentTransaction.commit();
}

When I'm in ResultsFragment and rotate the device, Activity onCreate method is called. Even though I got the previous InputDataFragment back from Bundle, calling FragmentTransaction.add() caused the loss of the data.

Moving FragmentTransaction code to the case where savedInstanceState == null was the solution.



Related Topics



Leave a reply



Submit