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
Pdf Not Open in Webview from Url
Retrofit 2 Json Response Json Array and Object
How to Get the Json Response of a Post Request in a Webview
Getting Net::Err_Connection_Refused (Http://Localhost:8080) on Android 4.4.2 Version
Android:Strike Out Text With Bold or Thicker Line Than Default Strike_Thru_Text_Flag
Alarm Manager Won't Work When the App Is Killed in the Background and Device Is Locked
Onclicklistener Does Not Work in Fragment
Import Android.Support.V7.App Cannot Be Resolved
How to Add/Remove Object in Recyclerview Using Arraylist in Android
Android:Get Current Date and Time from Firebase
How to Programmatically Change the Colour of a Textview, to a Colour Saved in an Attributes File
Detect or Prevent If User Uses Fake Location
React-Native: Module Appregistry Is Not a Registered Callable Module
Flutter:Renderbox Was Not Laid Out: Renderrepaintboundary#58C65 Relayoutboundary=Up1 Needs-Paint
Possible Unhandled Promise Rejection (Id:0) Error: Network Error in React Native