57

I was looking at the way Android handles orientation change for my application (I discovered that it restarts the mainactivity on orientation change. I've seen that you can override the method

protected void onSaveInstanceState(Bundle outState)

To save stuff, then have the in onStart. The problem is that I've my view with custom objects and a listview using a custom adapter. Everything is in a ArrayList of these objects, but I've noticed that you can't put arbitrary objects in the bundle! So how do I save the state?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
gotch4
  • 13,093
  • 29
  • 107
  • 170

6 Answers6

139

Have you tried using: its work around ,

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

in Manifest file?

It does not work by default because , when you change the orientation onCreate will be called again and it redraws your view.

If you write this parameter no need to handle in Activity , the framework will take care of rest of things. It will retain the state of the screen or layout if orientation is changed.

NOTE If you are using a different layout for landscape mode , by adding these parameters the layout for landscape mode will not be called.

Other way and Another way

Rahul Patil
  • 2,707
  • 2
  • 21
  • 32
  • 4
    Thanks a lot, this will help me so much! – Marius Feb 25 '13 at 08:25
  • 11
    Why isn't this the way it works by default? Who wants their data to be lost upon rotation? – CodeSlinger512 Feb 25 '13 at 17:33
  • 1
    By using the parameters `orientation` `onSaveInstanceState` and `onConfigurationChanged` are overridden . If you loose some thing when you change data -> scroll-> Change orientation the `screenSize` will take care. – Rahul Patil Feb 26 '13 at 04:40
  • 1
    @Davidguygc , it does not work by default because , when you change the orientation `onCreate` will be called again and it redraws your view. – Rahul Patil Jul 16 '13 at 08:38
  • 1
    Thanks I really appreciate this, you wouldn't believe the number of incorrect answers I've seen/tried for this. – KBusc Jul 31 '13 at 17:35
  • 40
    A big fat -1. "Perhaps the hackiest and most widely abused workaround is to disable the default destroy-and-recreate behavior by setting the android:configChanges attribute in your Android manifest." [Source](http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html) – Simon Forsberg Oct 29 '13 at 11:16
  • 2
    @SimonAndréForsberg , the article is newer one . And most of the developers understand that my answer is a work around , if they dont to use the parameter in manifest they can use the methods such as `onSaveInstanceState` `onRestoreInstanceState` and `onConfigurationChanged` . There are different ways to solve a problem and all have a different prespective towards solving a problem. – Rahul Patil Oct 29 '13 at 11:36
  • I echo Simon's comment. I have down voted as a result. Disabling configuration changes like this is a quick solution but it really defeats the purpose of the different resource management that android does automatically as a result of the current configuration. – curob Feb 23 '16 at 21:32
  • @RahulPatil Is there any attributes like this for fragment ? I've listviews in fragment – Vinoth Vino Sep 13 '16 at 02:48
48

EDIT: On newer versions of Android and with the compatibility library, retained fragments are usually the best way to handle keeping expensive-to-recreate data alive across activity destruction/creation. And as Dianne pointed out, retaining nonconfiguration data was for optimizing things like thumbnail generation that are nice to save for performance reasons but not critical to your activity functioning if they need to be redone - it's not a substitute for properly saving and restoring activity state.

But back when I first answered this in 2010:
If you want to retain your own (non view-state) data, you can actually pass an arbitrary object specifically for orientation changes using onRetainNonConfigurationInstance(). See this Android Developers blog post. Just be careful not to put any Views or other references to the pre-rotation Context/Activity in the object you pass, or you'll prevent those objects from being garbage collected and may eventually run out of memory (this is called a context leak).

Yoni Samlan
  • 37,905
  • 5
  • 60
  • 62
  • 8
    Note that onRetainNonConfigurationInstance() should *only* be used as an optimization. Your app should still work even if this doesn't happen... because there is *no* guarantee that it will, and in fact many situations where it definitely won't be called. You should first make your app work correctly through the full saved state mechanism, and only implement this method as an optimization. – hackbod Oct 12 '10 at 16:56
  • The above link is unavailable. Can you give me some other link? – Debopam Mitra Jul 29 '12 at 19:38
  • 1
    Yes, things changed over the course of the nearly three years since this was answered! These days a setRetainInstance Fragment is almost always a better call, if you're using Android 3.x+ or the compat library. – Yoni Samlan Jul 26 '13 at 22:21
18

First, you need to determine what is actually the "state" in your app. You haven't said what you are actually doing, but let me assume that the ArrayList of objects is the state the user is working with.

Second, decide what the lifecycle of this state actually is. Is it really tied to that activity? Or should the user not lose it if say their battery runs low, the device turns off, and they later return to your app? If the former, onSaveInstanceState() is correct; if the latter, you'll want to save to persistent storage in onPause().

For onSaveInstanceState() with custom objects, the key is to implement the Parcelable interface. This involves implementing the methods on Parcelable, as well as making a static CREATOR object in your class. Here's the code for a typical simple Parcelable class:

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java

The key functions are the Parcelable implementation:

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java#317

and the CREATOR static class:

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java#355

(The static writeToParcel() and readFromParcel() are just conveniences that were done for that class and not required.)

Now that you have that, you can put your entire ArrayList of objects into the saved state Bundle with Bundle.putParcelableArrayList:

http://developer.android.com/reference/android/os/Bundle.html#putParcelableArrayList(java.lang.String, java.util.ArrayList)

In Activity.onCreate(), check to see if you have a savedState Bundle, and if so try to retrieve the ArrayList from that and use it if found, creating a new adapter and list view for the new activity that are used to display it.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
hackbod
  • 90,665
  • 16
  • 140
  • 154
3

but I've noticed that you can't put arbitrary objects in the bundle!

First make your custom objects Parcelable.
Then you can put them in to a bundle.

Everything is in a ArrayList of these objects

You can use putParcelableArrayList method in bundle to store an ArrayList of custom "parcelable" objects.

Ε Г И І И О
  • 11,199
  • 1
  • 48
  • 63
3

write your objects to JSON Strings using Google's Gson, then save them as a String. Then, build them back from the saved JSON Strings when rebuilding the Activity/Fragment

import com.google.gson.Gson;

public class MyClass {
    private int a;
    private String s;
    private OtherSerializableClass other;
    private List<String> list;

    public String toJson() {
        return new Gson().toJson(this);
    }

    public static ChatAPI_MessagesArray fromJson(String json){
       return new Gson().fromJson(json, YourClass.class);
    }

    // Getters
    ...

    // Setters
    ...
}
ChrisMcJava
  • 2,145
  • 5
  • 25
  • 33
  • I have many layouts, textviews and buttons that are created programmatically in mainactivity. Does the solution you provided here covers my case or shall I use different approach? thanks – Eftekhari Jul 01 '15 at 20:03
0

you can use view model and the Model-View-ViewModel (MVVM) pattern to preserve Activity state during a screen rotation.

read more information by this linke in Android documents:

https://developer.android.com/topic/libraries/architecture/viewmodel

AmirMohamamd
  • 301
  • 3
  • 14