I came across two ways to prevent the activity from redrawing whenever the screen is rotated.
One is saveInstanceState + restoreInstanceState combo, which I still haven't been able to successfully implement.
The other one was to declare configChanges attribute in the activity tag of the manifest file to orientation
I'm curious as to what is the difference between the two.
In one scenarios you are handling all the
Related configuration changes without activity getting restarted.. while in second you are just taking care of your desired user state variables ie saving and restoring them.
When a configuration change occurs at runtime, the activity is shut down and restarted by default, but declaring a configuration with android:configChanges attribute will prevent the activity from being restarted. Instead, the activity remains running and its onConfigurationChanged() method is called, handling configuration changes your self is a complex task and should be avoided..
It is not recommended to handle configuration changes yourself due to the hidden complexity of handling the configuration changes.
However, if you are unable to preserve your UI state using the preferred options (onSaveInstanceState(), ViewModels, and persistent storage) you can instead prevent the system from restarting your activity during certain configuration changes. Your app will receive a callback when the configurations do change so that you can manually update your activity as necessary.
[Reference link]
I'm curious as to what is the difference between the two.
The difference between saveInstanceState + restoreInstanceState combo and configChanges is that configcahnges attribute in the manifest will prevent the activity from being destroyed when the specified change(s) occurred. In Android when you rotate the device or when the screen size change, the activity will be destroyed and recreated.
saveInstanceState + restoreInstanceState combo will not prevent the activity from being destroyed and recreated. In fact, they will just help you pass the data from the destroyed activity to the one which will be recreated.
Inside saveInstanceState you save all the data you want into a bundle object. Then, inside restoreInstanceState you get these data and use them in the recreated activity.
For example, if I use this attribute inside an activity tag in the Manifest file:
android:configChanges="orientation|screenSize"
Now, this activity won't be destroyed when the screen rotates and all the data of the activity will stay as is because it will not be destroyed.
saveInstanceState + restoreInstanceState combo, which I still haven't been able to successfully implement.
Here is how saveInstanceState + restoreInstanceState combo work:
Let's say I have a global variable called userScore holding the user scores in a game. Now, I did not use the attribute android:configChanges="orientation|screenSize" in my activity tag, so it will be destroyed if the user rotates the screen and the scores will be lost. To prevent them from being lost I will use saveInstanceState + restoreInstanceState like so:
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
outState.putInt("Score", userScore); // saving the userScore value
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
userScore = savedInstanceState.getInt("Score"); // restoring the userScore value
super.onRestoreInstanceState(savedInstanceState);
}
Both of the solutions have to do with data persistence during the lifecycle of your activity. Please read here more regarding LifeCycle
The basic difference is that when your app dies, with the save/restoreInstance combo (there are many different ways to do this) is that you can save the state of your app and when your activity is recreated after it was previously destroyed, you can recover your saved instance state from the Bundle that the system passes to your activity.
configChanges
Lists configuration changes that the activity will handle itself. When a configuration change occurs at runtime, the activity is shut down and restarted by default, but declaring a configuration with this attribute will prevent the activity from being restarted. source
Generally speaking i would suggest to NOT lock orientation in your app with the configChanges attr to prevent your activity from dying. Just save its state and restore through LifeCycle
Related
What is the significance of:
super.onCreate(null);
instead of
super.onCreate(savedInstanceState);
With this change, I am able to avoid many problems that otherwise plague my Activitys each time a configuration change occurs (rotation, locale shift, permission toggle). It seems that with this change, the Activity is started afresh whenever a configuration change triggers it to restart. And I don't seem to lose any data or process state by doing this: all my Activitys are restored exactly to their former state.
My question is, can I do this with impunity henceforth, or am losing something in the bargain? I don't really understand why this works, whether it is safe or not, and what unintended effects it may have on my app.
I chanced upon this trick here.
Related Questions:
Calling super.onCreate() with null parameter?
Will 'Bundle savedInstanceState' be alive after Application is being killed?
Activity state instance - insights?
Activity's instance state: what is automatically stored and restored
onCreate() call first when activity is about to create, Also Android System manage activity lifecycle and can kill the activity with saving its instanceState, in case if acitvity out of focus for user for long time and system is on low memory situation.
An activity has essentially four states
super.onCreate(null) : Would always create activity as it is creating fisrt time , even Android system provide its savedInstanceState, and does't matter what orientation configurations are.
super.onCreate(savedInstanceState) : Activity can use 'savedInstanceState' to reset its state or component where it was last.
To achive this, acitivty's instance state need to be persist before activity lost user attention( that could be onStop or onDestroy)
savedInstaceState can also be important to handle if activity configuration got changed, Please check acitvity life cycle behavior on Configuration change
am losing something in the bargain?
Only if you are working with Fragments. see Calling super.onCreate() with null parameter?
Yes, onCreate(...) is necessary to start an Activity, but passing Bundle as an argument is required when you are working with fragments.
What did you infer from that?
The argument savedInstanceState is anyway null by default. So you aren't really losing anything in a bargain.
But wait, we usually use Bundles to maintain orientation change, right?
the following manifest code declares an activity that handles both the screen orientation change and keyboard availability change:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="#string/app_name">
Now, when one of these configurations change, MyActivity does not restart. Instead, the MyActivity 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.
the following onConfigurationChanged() implementation checks the current device orientation:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
But Remember: When you declare your activity to handle a configuration change, you are responsible for resetting any elements for which you provide alternatives. If you declare your activity to handle the orientation change and have images that should change between landscape and portrait, you must re-assign each resource to each element during onConfigurationChanged().
As far as I know a lot of data is saved in the bundle savedInstanceState.
E.g. all the views' states in your current layout, such as the current content of any EditText or CheckBox.
You could also look up some official sources to check whether you need to keep some data.
Here's a nice article about it
Basically it says that all View class implements the methods onRestoreInstanceState and onSaveInstanceState, which are saving and restoring any temporary states they were in before the state change.
The savedInstanceState is a reference to a Bundle object that is passed into the onCreate method of every Android Activity. Activities have the ability, under special circumstances, to restore themselves to a previous state using the data stored in this bundle.
It is very important to use savedInstantState to get values from Intent which is saved in the bundle.
All your data stored in class variables or local variables is lost whenever you change rotation of device, but in your activity it looks like you have not stored any data as long as user enters any data, but instead you are perhaps reading data on click of a button or something like that, your activity will behave and work normally, and all user inputs like text inside EditText will be restored by Android itself, because it identifies "IDs" (android:id="#+id/anyID") allotted to each view and can restore by itself all the values inserted by user.
I hope this this helps you...
Happy coding :)
I am not understanding how android activities are managed.
I have an activity and every once in a while i have noticed that, when my app goes into the background, android destroys whatever the current activity is (say Activity3) and several other singletons and objects etc. Thats fine. The problem is when the app is resumed then intuition tells me that since android has destroyed the activity and objects for memory or whatever, then android would just restart the app completely from Activity1 so all the objects and data members would get properly initalized.
NOT SO!
It seems that when my app is resumed, the Activity3 is recreated and onCreate is called with the same parameters as it was the first time (when it was called from Activity2) only this time all the singletons and other objects that were initialized in Activity1 and Activity2 are recreated with their default values and are rendered useless.
How is this a safe policy/technique? How can android just randomly destroy objects and activities and then when the user resumes just call onCreate on the recent activity and expect everything to be hunky doory and NOT have to go through the proper startup procedure/initialization?
UPDATE / SOLUTION
Thanks to the commentors for their excellent info.
ACCORDING TO ANDROID DOCUMENTATION
onCreate
Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.
THEREFORE what I ended up doing is I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or Auto-Rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.
public class SomeMiddleActivity extends AppCompatActivity
{
private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating
#Override
public void onSaveInstanceState(Bundle state)
{
// set a flag so that onCreate knows this is valid
state.putBoolean("StateSaved", true);
super.onSaveInstanceState(state);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
// this must be called first always for some reason
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
{
// we were recreated... start app over
Intent intent = new Intent(getApplicationContext(), Startup.class);
startActivity(intent);
finish();
return;
}
}
bInit = true; // this will stay true until android has cleared our memory
.......
}
Although this has worked thus far, if anyone has a different suggestion let me know. I will be posting another article on this.
And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)
#goldenb #Rishabh Thanks to goldenb and Rishabh for the insight.
Android, if destroys, also gives you tools to handle it.
Mobile devices have limited amount of memory which needs to be shared among Applications running simultaneously. Thus, smart resource allocation is necessary. The Apps running on foreground are used by End-User and gain high priority for better performance and user experience. Thus, applications running in background need to release the resources to suffice the memory requirements for foreground applications. Hence, background applications are destroyed (not completely) sometimes (in case of low memory).
Android Activities have Callbacks likes onSaveInstanceState() and onRestoreInstanceState() which enable you to save your current state of Activity (i.e., values of variables) when it is destroyed and retrieve them when the Activity is recreated.
You can get more information from here: How to save and retrieve the state of Activity using onSaveInstanceState and onRestoreInstanceState.
You can perform validations on the retreived state to ensure the Activity performs exactly as it was doing pre-destruction. You would find it very easy and logical once you get hands-on it.
Just giving my 50 cents on the issue. The correct way to deal with the issue of an activity being killed by the system for its resources in background is a common problem in android and according to Google the solution for this is:
onPause() is where you deal with the user leaving your activity. Most
importantly, any changes made by the user should at this point be
committed (usually to the ContentProvider holding the data).
Emphasis is mine. But what this means is that the Android lifecycles are designed so that under normal conditions onPause should be called as an Activity or Fragment is sent to the background. They hint at this in several of the android documentation pages:
As your activity enters the paused state, the system calls the onPause() method on your Activity, which allows you to stop ongoing actions that should not continue while paused (such as a video) or persist any information that should be permanently saved in case the user continues to leave your app.
Also worthy of your attention: if you wish that views are restored during Activity recreation, you should have set the ID attribute of all views ;)
Note: In order for the Android system to restore the state of the
views in your activity, each view must have a unique ID, supplied by
the android:id attribute.
PS.
You were wondering why onSaveInstanceState(Bundle, PersistableBundle) is not called, one possibility is that you do not have the right activity attribute set
This is the same as onRestoreInstanceState(Bundle) but is called for
activities created with the attribute persistableMode set to
persistAcrossReboots..
How does android restart an activity or fragment when rotating the app?
I'm interested in the methods or flags it uses in the process. Thx
When any configuration changes, such as device orientation takes place the activity will be destroyed and recreated that is unless you've modified this process inside the manifest file. As #Lazai mentioned, if you change activity configuration changes functionality, you must handle manually loading any new resources required for the new orientation, that includes, styles, themes, drawables, and layouts inside of the callback Activity.onConfigurationChanged(Configuration newConfig).
Note: if you don't indicate inside of the manifest file that you'd like to manually handle configuration changes, you will never received a call to Activity.onConfigurationChanged(Configuration newConfig).
Android exports recommend not handling the configuration changes yourself and letting the OS handle itself. So how to know when orientation changes are taking place when you don't get calls to onConfigurationChanged(Configuration newConfig)? Well if you're are targeting above API Level 11, their is a handy method on the Activity class that indicates if activity is experiencing the a configuration change, called Activity.isChangingConfigurations(). This method will always return false until the activity is preparing to be destroyed. It will have a valid value prior to the call Activity.onPause(), that you can check and determine if your device is rotating and your app should perform some special optimizations or state saving procedures.
I personally recommend letting the system handling the config changes and checking if the orientation is changing, because in a large app or complex activity it can get very tedious reloading the necessary resources and assets just to prevent an activity from discarding a simple object or during rotations.
When your orientation changes all the fragments and activities are destroyed , and the views are re-created again ; unless you change the config settings :
<activity android:name=".SampleActivity"
android:configChanges="orientation|keyboardHidden">
So basically the key here is onStop() and onDestroy() , you should save the states of your tasks when rotating in the onStop() method (or even onPause()) to restart them again when the views are loaded (onResume()).
See more here link
When Activities and Fragments rotate, they are destroyed (with onDestroy()) then they go through the same lifecycle as they were created.
in your fragment override onconfigurationchanged method
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
// reload your views here
}
I am an Android Beginner. According to the lifecycle of an android application, when screen orientation is changed following methods are called:
onPause()
onStop()
onDestroy()
OnCreate()
onStart()
onResume()
But I have now added onConfigChanged in my XML file. When activity starts, onCreate, onStart and onResume are called. But when my screen orientation changes, only onConfigurationChanged method is called, the above mentioned methods are not called as shown below:
Any reason for this?
Thanks in advance.
Normally when an orientation , language , ... change happen the activity get destroyed and restarted and start the lifecycle from the begining unless you override the onConfigurationChanged function.
More details from Android documentation:
Configuration Changes
If the configuration of the device (as defined by the
Resources.Configuration class) changes, then anything displaying a
user interface will need to update to match that configuration.
Because Activity is the primary mechanism for interacting with the
user, it includes special support for handling configuration changes.
Unless you specify otherwise, a configuration change (such as a change
in screen orientation, language, input devices, etc) will cause your
current activity to be destroyed, going through the normal activity
lifecycle process of onPause(), onStop(), and onDestroy() as
appropriate. If the activity had been in the foreground or visible to
the user, once onDestroy() is called in that instance then a new
instance of the activity will be created, with whatever
savedInstanceState the previous instance had generated from
onSaveInstanceState(Bundle).
This is done because any application resource, including layout files,
can change based on any configuration value. Thus the only safe way to
handle a configuration change is to re-retrieve all resources,
including layouts, drawables, and strings. Because activities must
already know how to save their state and re-create themselves from
that state, this is a convenient way to have an activity restart
itself with a new configuration.
In some special cases, you may want to bypass restarting of your
activity based on one or more types of configuration changes. This is
done with the android:configChanges attribute in its manifest. For any
types of configuration changes you say that you handle there, you will
receive a call to your current activity's
onConfigurationChanged(Configuration) method instead of being
restarted. If a configuration change involves any that you do not
handle, however, the activity will still be restarted and
onConfigurationChanged(Configuration) will not be called.
It's a very good thing that only onConfigurationChanged() is called with orientation changes. It's actually preferred because it means that your initializations in onCreate() DON'T have to happen again -- you can reuse them with the new views that you rebind in onConfigurationChanged(). This results in a faster application that uses less memory and is less clunky.
Case in point:
You have a ListView. That ListView uses an ArrayAdapter. In onCreate(), you find the listView using findViewById(R.id.xxx) and you CREATE a NEW ArrayAdapter and set it for that listView. If every time onCreate() is called when the orientation changes, you would be creatting a new ArrayAdapter each and every time the orientation changes. Imagine switching the orientation 3 times -- what a waste!
Like this, you can create the ArrayAdapter in onCreate(), once, ever (when your application is created). And when the orientation changes, in onConfigurationChanged(), simply reset the ArrayAdapter for the new ListView you pick up again using findViewById(R.id.xxxx). Done. Only 1 ArrayAdapter has ever been created and it's data remains in tact for the new orientation.
Beautiful! :)
I have a small activity with an EditText and an imageview and a button. When you press the button it launches camera for result, and when it returns it changes the imageview to the picture you've just taken.
But when the orientation changes, the imageview resets back to the default one on the layout.
What I tried doing was setting a boolean called custom, and when you take a picture it sets it to true. I overrid onConfigurationChanged() and if custom is set to true I restore the image.
My problem now is the EditText becomes erased -- How can I restore the EditText after configuration change? My first attempt was storing it's content into a String onPause() and then restoring it, but it always comes up blank.
Usually when UI view does not keep its state, first thing to check is that this UI view has id assigned. Without this id views cannot restore their state.
<EditText android:id="#+id/text" ... />
If this doesn't help, you need to save and restore state yourself. Take a look at Handling Runtime Changes. It pretty much explains what you should do:
To properly handle a restart, it is important that your Activity restores its previous state through the normal Activity lifecycle, in which Android calls onSaveInstanceState() before it destroys your Activity so that you can save data about the application state. You can then restore the state during onCreate() or onRestoreInstanceState(). To test that your application restarts itself with the application state intact, you should invoke configuration changes (such as changing the screen orientation) while performing various tasks in your application.
You should override onSaveInstanceState() and save your Acitivity state when its called:
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString("textKey", mEditText.getText().toString());
}
And then restore state in onCreate() or onRestoreInstanceState():
public void onCreate(Bundle savedInstanceState)
{
if(savedInstanceState != null)
{
mEditText.setText(savedInstanceState.getString("textKey"));
}
}
If this is still not sufficient, you can override onRetainNonConfigurationInstance() and return any custom Object that will be passed to new activity object, when its recreated. More details about how to use it can be found in Handling Runtime Changes. But this function is deprecated in Android 3.0+ (specifically for FragmentActivity where its final). So this cannot be used together with Fragments (which is fine, they have their mechanism to retain objects accross configuration changes).
And final one - never use android:configChanges. You must have very good reasons to use it, and usually these are performance reasons. It wasn't meant to be abused the way it is now: just to prevent UI state reset. If this attribute is used, then yes, Activity UI will not be re-set on config change, but Activity state still will be reset when destroyed and re-created later.
The documentation explains this option pretty well:
Note: Handling the configuration change yourself can make it much more
difficult to use alternative resources, because the system does not
automatically apply them for you. This technique should be considered
a last resort and is not recommended for most applications
You can force your activity not to reload after orientation changed by adding android:configChanges="orientation" to your activity line in manifest.