Handle screen rotation without losing data - Android
Asked Answered
U

8

53

I'm becoming crazy figuring out what is the best way to handle screen rotation. I read hundreds of questions/answers here but I'm really confused.

How can I save myClass data before the activity is re-created so I can keep everything for redrawing activity without another useless initialization?

Is there a cleaner and better way than parcelable?

I need to handle rotation because I want to change layout in Landscape mode.

public class MtgoLifecounterActivity extends Activity {

    MyClass myClass;

    // Called when the activity is first created
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        If ( ?? first run...myClass == null ? ) {
            myClass = new MyClass();
        } else {
            // do other stuff but I need myClass istance with all values.
        }
        // I want that this is called only first time. 
        // then in case of rotation of screen, i want to restore the other instance of myClass which
        // is full of data.
    }
Uke answered 12/4, 2012 at 15:31 Comment(1)
Use the onConfigurationChanged stuff, see: #456711Phrase
R
29

can use override method onSaveInstanceState() and onRestoreInstanceState(). or to stop calling onCreate() on screen rotation just add this line in your manifest xml android:configChanges="keyboardHidden|orientation"

note: your custom class must implements Parcelable example below.

@Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable("obj", myClass);
    }

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
 // TODO Auto-generated method stub
 super.onRestoreInstanceState(savedInstanceState);
 myClass=savedInstanceState.getParcelable("obj"));
}

public class MyParcelable implements Parcelable {
     private int mData;

 public int describeContents() {
     return 0;
 }

 /** save object in parcel */
 public void writeToParcel(Parcel out, int flags) {
     out.writeInt(mData);
 }

 public static final Parcelable.Creator<MyParcelable> CREATOR
         = new Parcelable.Creator<MyParcelable>() {
     public MyParcelable createFromParcel(Parcel in) {
         return new MyParcelable(in);
     }

     public MyParcelable[] newArray(int size) {
         return new MyParcelable[size];
     }
 };

 /** recreate object from parcel */
 private MyParcelable(Parcel in) {
     mData = in.readInt();
 }


}
Risley answered 12/4, 2012 at 15:37 Comment(3)
but what I need to do onSaveInstanceState and onRestoreInstanceState ? Implements the parcelable?Uke
pffff not exist any method + easy ? I need parce my class InstantAutoComplete that extends from AutoCompleteTextView extends from EditText implements Filter.FilterListener .... I lose 1 day to get all values ....Whitted
super.onSaveInstanceState(outState); is called in the end.Fugitive
D
52

In Activity Tag of Manifest you should have to mention

<activity
        android:name="com.example.ListActivity"
        android:label="@string/app_name" 
        android:configChanges="keyboardHidden|orientation">

If you are using Android 2.3(API level 13 ) and above use

<activity
        android:name="com.example.Activity"
        android:label="@string/app_name" 
        android:configChanges="keyboardHidden|orientation|screenSize">

It should have to work.

It will work only with activity tag and not with application tag

Dipterocarpaceous answered 29/11, 2013 at 6:22 Comment(2)
My problem was in screenSize attribute, thanks PrashantBoorer
This is a bad idea. It will break resource lookups that rely on Activity recreation happening on size changes, as well as fail to correctly handle other configuration changes.Hardnett
R
29

can use override method onSaveInstanceState() and onRestoreInstanceState(). or to stop calling onCreate() on screen rotation just add this line in your manifest xml android:configChanges="keyboardHidden|orientation"

note: your custom class must implements Parcelable example below.

@Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable("obj", myClass);
    }

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
 // TODO Auto-generated method stub
 super.onRestoreInstanceState(savedInstanceState);
 myClass=savedInstanceState.getParcelable("obj"));
}

public class MyParcelable implements Parcelable {
     private int mData;

 public int describeContents() {
     return 0;
 }

 /** save object in parcel */
 public void writeToParcel(Parcel out, int flags) {
     out.writeInt(mData);
 }

 public static final Parcelable.Creator<MyParcelable> CREATOR
         = new Parcelable.Creator<MyParcelable>() {
     public MyParcelable createFromParcel(Parcel in) {
         return new MyParcelable(in);
     }

     public MyParcelable[] newArray(int size) {
         return new MyParcelable[size];
     }
 };

 /** recreate object from parcel */
 private MyParcelable(Parcel in) {
     mData = in.readInt();
 }


}
Risley answered 12/4, 2012 at 15:37 Comment(3)
but what I need to do onSaveInstanceState and onRestoreInstanceState ? Implements the parcelable?Uke
pffff not exist any method + easy ? I need parce my class InstantAutoComplete that extends from AutoCompleteTextView extends from EditText implements Filter.FilterListener .... I lose 1 day to get all values ....Whitted
super.onSaveInstanceState(outState); is called in the end.Fugitive
C
18

The problem here is that you are losing the "state" of the App. In OOPs, What is a State? The Variables! Exactly! Hence when you are losing the data of your variables.

Now here is what you can do, Find Out the variables which are losing their states.

enter image description here

When you rotate your device, your present activity gets completely destroyed, ie goes through onSaveInstanceState() onPause() onStop() onDestroy() and a new activity is created completely which goes through onCreate() onStart() onRestoreInstanceState.

The Two Methods in the bold, onSaveInstanceState() saves the instance of the present activity which is going to be destroyed. onRestoreInstanceState This method restores the saved state of the previous activity. This way you don't lose your previous state of the app.

Here is how you use these methods.

 @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);

        outState.putString("theWord", theWord); // Saving the Variable theWord
        outState.putStringArrayList("fiveDefns", fiveDefns); // Saving the ArrayList fiveDefns
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onRestoreInstanceState(savedInstanceState, persistentState);

        theWord = savedInstanceState.getString("theWord"); // Restoring theWord
        fiveDefns = savedInstanceState.getStringArrayList("fiveDefns"); //Restoring fiveDefns
    }

EDIT : A Better Approach: The above approach toward maintaining your data isn't the best way to maintain data in production code/apps. Google IO 2017 introduced ViewModel to protect your data against configurational changes (like Screen Rotation). Keeping all data inside the activity using variables isn't a good software design and violates the Single Responsibility Principle hence separate your data storage using ViewModel from the activities.

  • ViewModel will be responsible for the data to be displayed.
  • Activity will be responsible for how to display the data.
  • Use additional repository class if you have an increasing complexity of storing the data.

That's just one of the way to separate classes and their responsibility, which will go a long way while making well-architectured apps.

Cucurbit answered 25/7, 2017 at 5:7 Comment(1)
You should override public void onRestoreInstanceState(Bundle savedInstanceState) instead of public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)Mcquiston
F
17

May be this is solved already but just for a small update for new members who stuck on it, just have a look at Google Developer Site, From API level 13, you just need to add this code to Manifest:

<activity android:name=".SplashScreensActivity"
          android:configChanges="orientation|keyboardHidden|screenSize"
          android:label="@string/app_name">

when one of these configurations change, SplashScreensActivity does not restart. Instead, the SplashScreensActivity receives a call to onConfigurationChanged(). This method is passed a Configuration object that specifies the new device configuration. By reading fields in the Configuration, you can determine the new configuration and make appropriate changes by updating the resources used in your interface. At the time this method is called, your activity's Resources object is updated to return resources based on the new configuration, so you can easily reset elements of your UI without the system restarting your activity.

Floorboard answered 14/1, 2015 at 7:37 Comment(1)
This is a bad idea. It will break resource lookups that rely on Activity recreation happening on size changes, as well as fail to correctly handle other configuration changes.Hardnett
V
5

There are two (good) ways about this. Make your class implement Parcelable and put it in a bundle in onSaveInstanceState(), or, if it's more complex (e.g. an AsyncTask), return it in onRetainNonConfigurationInstance().

Then there's also the lazy way where you just stop reacting to configuration changes.

Vaillancourt answered 12/4, 2012 at 15:37 Comment(3)
this seems to be deprecated! Yes, it's quite complex object - onRetainNonConfigurationInstance()Uke
Yup, they deprecated onRetainNonConfigurationInstance() but didn't provide an alternative, as far as I could tell.Vaillancourt
@Vaillancourt could use a fragment without UI as the alternative: developer.android.com/reference/android/app/…Timbering
S
4

If you do not have a need for your activity to be restarted just set the configChanges attribute on your activity in the AndroidManifest.xml to this:

    android:configChanges="keyboard|keyboardHidden|orientation"

This will tell the OS that you are going to take care of handling a rotation and will not restart your activity. Using this method will remove the need for you to have to save any kind of state.

Snooperscope answered 12/4, 2012 at 15:37 Comment(2)
This is not the case, the Google dev's have advised against this practice as this is assuming that orientation change is the only reason the Activity may be disposed and re-created when infact there are a number of other reasons why this may occur.Pinkerton
This is a bad idea. It will break resource lookups that rely on Activity recreation happening on size changes, as well as fail to correctly handle other configuration changes.Hardnett
F
0

On changing screen configuration we can make sure to not make any data loss by,

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)


    // Check the new orientation
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // TODO: On Landscape mode
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        // TODO: On Portrait mode
    }
}
Ferriage answered 18/7 at 19:2 Comment(0)
H
-1

from the documentation:

Note: If your application targets Android 3.2 (API level 13) or higher, then you should also declare the "screenSize" and "screenLayout" configurations, because they might also change when a device switches between portrait and landscape orientations.

so what you shpuld write will be :

<android:configChanges="keyboardHidden|orientation|screenSize|screenLayout">
Hives answered 9/12, 2019 at 17:32 Comment(1)
This is a bad idea. It will break resource lookups that rely on Activity recreation happening on size changes, as well as fail to correctly handle other configuration changes.Hardnett

© 2022 - 2024 — McMap. All rights reserved.