Storing complex computed data between orientation change - android

In the onCreate() of my MainActivity, my app does an intensive operation to generate some data set (runs in a separate thread, but takes some 2 - 3 seconds to complete normally). Now my problem is that, when orientation changes, the app again does this complex computation again.
Since I haven't done anything like this before, I was wondering if there is a way around this. My first thought was to store the computed data in a static variable, so that the data is persisted between different instances of MainActivity. I am guessing this is not the best approach.
My data set consists of a Map and an ArrayList and not a simple data type, if it helps.
I have looked at the onSaveInstanceState(), but it only provides to store values like int, String, etc.

In addition to what #Raghunandan suggested you can read the official docs on Recreating an Activity .
It says:
Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout.
It also introduces the concept of
onSaveInstanceState()(To save additional data about the activity state, you must override this method) and
onRestoreInstanceState() method (The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle)).
You can also see the sample implementation of these methods here Saving Android Activity state using Save Instance State thread, which will help you to save and restore the state.
Update
You may also try using:
<activity name= ".YourActivity" android:configChanges="orientation|screenSize"/>
The docs for android:configChanges say;
It 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: How to save state during orientation change in Android if the state is made of my classes?
Hope this helps.

Related

Can I trust my global variables to always be preserved during screen rotation? (Android / Kotlin)

Is it bad practice to use Global variables instead of bundles to preserve state between screen rotations?
I really like just using global variables to keep state. But since everything suggests using bundles, should I do that? I don't know what pitfalls I could be thinking of.
Here is a snippet of code that demonstrates global variables dont change.
I have to check that the LEVEL_DEFS list is 0 before adding to it, otherwise every screen rotation adds duplicate levels (where I originally assumed I would have to re-add the levels after rotation)
`
val LEVEL_FILES = arrayOf("levels/lv1.txt", "levels/lv2.txt", "levels/lv3.txt")
val LEVEL_DEFS = mutableListOf<LevelDefinition>()
fun getAllLevels(context: Context): MutableList<Level> {
if (LEVEL_DEFS.size == 0) {
for (filename in LEVEL_FILES) {
LEVEL_DEFS.add(LevelDefinition(getTextFileString(context, filename)))
}
}
}
`
When I google screen rotation, all the results suggest that you have to use a bundle to save state.
I also asked chat gpt-3, which insisted that I must use a bundle to save the state during rotation.
Yet, it is clear to me that global variables do not change during rotation. I have used many simulated and real devices and they all behave the same way.
Your global variables will live as long as their scope is in memory, so it depends where you're storing them - if they're at the top level (e.g. defined in the top level of a file, or in an Application component) that state will exist until the process is destroyed.
If your state is stored in an Activity, then that Activity will be destroyed on rotation, so typically you'd use the Bundle passed into onSaveInstanceState to store any data you need, and then restore it when appropriate.
If your app is in the background, its process can be destroyed at any time by the system - in that case, you'd lose your global variable state. For an Activity, those save state lifecycle methods would be called, so you'd still store in the Bundle and receive that Bundle when the app is opened again. The process will have been destroyed, but if you're set up to store and restore that state, you'll handle it just like with a rotation. (This is a different situation to the user actually closing the app, which considers the next time it's opened to be a "fresh start" and doesn't provide any stored state.)
You can also use View Models to store state, which survive rotation but require using SavedStateHandle to behave like the Bundle and survive process death (but again, not actual restarts). More info about your options for storing this transient state here.
So what this means is it depends exactly what you're storing, how long it needs to stick around, and if it's allowed to be destroyed when the app is in the background. That last one is usually a "no" which makes global variables a bad fit in most cases.
You'll need some way to persist that data (e.g. SharedPreferences) and at that point you may as well use that as your state, rather than storing it there and in a variable. Up to you though! Persisting data is also how you'd handle state surviving fresh start app restarts too, e.g. storing a high score that shouldn't disappear when the user closes the app.
(Also I'm not sure what asking a chatbot for advice is worth, no matter how much it "insists"! But that's a whole other topic)
Global variables persist as long as your application is running - however when it is in the background you have no guarantee that the Android OS won't stop and re-start your application (which would lose any global or static variables). Once your application goes to the background, you can no longer be certain they will stick around.
What you have posted looks like it would be fine though if you only access it through the getAllLevels method (since it re-creates it when needed). However, if you are adding levels somewhere else, those could be lost.
If you want to be able to add levels, one possible solution would be to load the data from SharedPreferences inside getAllLevels when the list is empty - and update the SharedPreferences any time levels are added.

Android Activity instance state

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 :)

android screen orientation conflict

I have a method in my main class, that fetches some data from the internet. The thing is that after everything is done, if I change the screen orientation by moving the device, everything starts allover again(fetching data while displaying a loading screen). Is there somewhere I could put my method so that if my device's screen orientation changes, it won't erase everything that has been done until that moment? Thanks.
What is happening to you is that every time you rotate your activity is recreated, as per android good practices you should handle your activity being recreated because android may destroy your activity at any point if resources go low on the device. Take a look at saving the state of your activity and how to restore it and the link.
Example using onSaveInstanceState()
You can use a singleton class to store your data.
If you prefer a simpler way you can also put your data as static, so the orientation change will not throw them away.
I think that your Activity is getting recreated again. In that case,it will load again.
1). You can handle orientation change by overriding
public void onConfigurationChanged(Configuration newConfig)
and in your activity declaration in manifest file add the following line
android:configChanges="orientation|screenSize|keyboardHidden"
2). As Aerilys said in the above answer, you can use singleton class to store data. Before displaying your loading screen check if you single ton object has data or not. If yes then skip displaying your loading screen

Why should I need setRetainInstance or onSaveInstance if I can use android:configChanges="keyboard|orientation|screenLayout"

Why should I need to use setRetainInstance() or onSaveInstance() to save state and I can use android:configChanges="keyboard|orientation|screenLayout" and get the samething "saving state non-UI state"? I mean with less headache.
Don't use android:configChanges. It will break things in subtle ways and will prevent Android from getting the proper layout/theme/dimensions, etc. for the current configuration.
onSaveInstanceState() is completely orthogonal to this: you need to save state so you can restore it if Android kills your process to save memory. configChagnes only prevents it from re-creating the activity on rotation, keyboard state changes, etc.
setRetainInstance() is for fragments that you don't want re-created on device rotation, etc. If you don't call it, Android will serialize their state in a Bundle, and re-create them along with the parent activity.
In short, while configChanges appears to be a 'shortcut' it is not. Don't rely on it and save/restore state as necessary using the proper tools for each case.

Android orientation changing -> Database dropped

I'm haveing some problems with orientation changes. When my app changes its orientation, database is cleared. Is it something I'm doing wrong or is just its default behaivour and I have to restore and save database with onSaveInstanceState()
Thanks
open your manifest.xml file and change Activity<> tag as below: then check
<activity
android:configChanges="keyboardHidden|orientation"></activity>
If you are recreating the database in onCreate, then you will see this behaviour unless you also implement onSaveInstanceState and either onRestoreInstanceState or use the SavedInstanceState arg to onCreate to restore what you previously saved (or both).
The whole point of Save/RestoreInstanceState is for those times where the system needs to kill and recreate your activity without the user knowing, so you need to preserve the illusion that you have been running all along. The two most likely instances where this will occur are
on an orientation change
if your activity is in the background and the system is running low on memory.

Categories

Resources