Handle screen rotation without losing data - Android
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.
}
Solution 1:
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
Solution 2:
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();
}
}
Solution 3:
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.
Solution 4:
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.
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.