I had an issue where my app kept toasting messages that I had put inside Oncreate() in my main activity which made me think that for some reason my app was restarting due to an exception. After much time I realised that this was happening because I was setting my app to landscape mode. Now that with the orientation change and the activity getting recreated twice, will it double the objects i'm creating and leaving performance issues on my app?
Your Activity will be destroyed, and all it's members will get garbage collected (if there are no other references to them). Therefore, never keep a reference to your Activity outside its own scope, or it will not get garbage collected!
Depending on the type of objects you're creating, you can use onSaveInstanceState and onRestoreInstanceState to save and restore them, obviously. This might speed up things a little (for example, instead of re-reading objects from a database).
Note that this will recreate the objects as well, instead of referring to the same object. It just might be a bit quicker.
Related
What's the best way to save any changes to global state, that would be lost if the process was killed due to low memory etc?
For activities we have onSaveInstanceState() which allows state to be saved when needed.
I would like something similar for global state. Our Application class holds a references to many objects that can be simply reloaded in onCreate() when the app next starts. But we also change the state of some of those objects, and require these changes to be maintained through the app being killed due to low memory etc.
We could of course persist every time changes are made to the objects in memory. But this would be very wasteful.
My current thought is to use onActivitySaveInstanceState() and keep a dirty flag to know only to call it once. But this adds complexity and probably isn't what this method was intended for.
This cannot be a particularly uncommon requirement, but most of the resources on the net focus on just Activity lifecycle. I'd be pleased to learn of a better approach.
There is no concept of Global State and I actually think that you don't need it at all. An app consists of several independent parts which have their own "state". Thus each part is responsible for its own state. For instance, a fragment which shows particular data knows where to get this data and what to do if there is no such data. An activity which shows user settings knows where to get user settings, how to update it and so on. Both of these parts know what to do when lifecycle methods are calling.
You can always store values in your Application class. On a high level, this is very simple, you create a custom MyCustomApplication, that extends the Android Application class. The Applicaiton class only lives, while the App is in scope (including when it's in the background, which is somewhat unpredictable in Android).
Then you would can access this using the getContext().getApplication()
The implementation details are more complex, and you really should extend the Applicaiton into a Singleton, as described in this SO post: Singletons vs. Application Context in Android?
Considering this scenario: If I created an activity and it moves to the background and this activity contains a Fragment which is set to setRetainInstance(true) then the Android OS might at some point still decide to shut down the activity's hosting process in order to free memory.
Then the Activity's state is saved via onSaveInstanceState(Bundle) where - as far as I understood - the related Bundle is written and to the file system to survive the process shut down. (thus the requirement of objects in the bundle being Serializable). Later, the applications state can be retrieved in a new process via onRestoreInstanceState(Bundle).
In contrast, my Fragment is allowed to contain variables which are not necessarily Serializable. Therefore, I figured, the Fragment cannot be stored on disk like the Bundle is. So what happens to my fragment when the process gets killed?
I was wondering about this reading the developer's guide (http://developer.android.com/guide/components/processes-and-threads.html):
A process holding an activity that's not currently visible to the user
(the activity's onStop() method has been called). These processes have
no direct impact on the user experience, and the system can kill them
at any time to reclaim memory for a foreground, visible, or service
process. Usually there are many background processes running, so they
are kept in an LRU (least recently used) list to ensure that the
process with the activity that was most recently seen by the user is
the last to be killed. If an activity implements its lifecycle methods
correctly, and saves its current state, killing its process will not
have a visible effect on the user experience, because when the user
navigates back to the activity, the activity restores all of its
visible state.
I understood the above killing such that the VM instance is shut down and the state of the process is written to the file system (here comes the Bundle into play). Later the bundles are read for resuming the process. Since the retaining of fragments is not concerned with life cycle methods and since I would not know how to retain e.g. a pointer to a network connection (you should of course never have such a pointer in a fragment anyhow), I was wondering if the fragments are still restored if the process is shut down in the meantime. I concluded that they surely needed to be recreated and that the life cycle methods are therefore to be preferred over setRetainInstance(true) whenever possible.
Does this assumption make any sense?
Sounds like you're mixing up two concepts here.
Saving state across Configuration Changes does not involve serialization. If you request setRetainInstance() for a Fragment then that means it will fully stay in memory and not be re-created only for configuration changes. A similar mechanism is available for Activity objects but they need to explicitly define an Object which is going to be saved. This works via Activity.onRetainNonConfigurationInstance(), not via onSaveInstanceStae().
The other mechanism involves serialization and possibly (maybe not always, not sure) file system I/O to be able to reproduce state information even if an Activity/Fragment is destroyed (which happens independently of its hosting Process, btw). This works via Activity.onSaveInstanceState() and Fragment.onSaveInstanceState().
Of course, you can use the second mechanism for the purpose of the first, thus slowing down the way your app deals with configuration changes. Depending on your internal state, the slowdown could me marginal of significant.
Regarding your questions.
"My Fragment in contrast, is allowed to contain variables which are not serializable." Well, the same is true for your Activity. It can contain non-serializable objects which can be saved across config changes as described above.
"the fragment cannot be stored to disk when a process is shut down and must be recreated when an activity was restored." No, both mechanisms are available for both object types.
Hope I could contribute to clarifying this a bit.
Edit after your first comment.
Regarding the comment:
"onRetainNonConfigurationInstance is deprecated": Yes. I mentioned it for demonstration purposes because of a specific wording in your question. Also, with Android 2 devices having a 46% market share as per today (official Google figures), this method will definitely stay around for a very long time, deprecated or not.
"My main concern is about what will happen to the fragment instance when my hosting process is killed and removed from the memory": Your fragment instance will be removed from memory and there's of course no way it is restored as-is with its complete internal state automatically. This is only done when you setRetainInstanceState in the case of config changes. (But note that this relates to the Instance, in other words, the full object.)
Regarding your edit:
Once more, yes, your Fragment's Bundle will be stored and restored to/from the Bundle regardless of setRetainInstanceState if you use Fragment.onSaveInstanceState() for this purpose, for everything that makes sense.
It is not true that "all of its visible state" will be saved as the text you refer to claims; for example, the visibility attribute will not be saved. Whether that's supposed to be a bug or a feature I don't know, but it's a fact. But this is only a side remark; UI elements will save most of their relevant state.
"the state of the process is written to the file system": No! The state of objects which are able to save their state to a Bundle and actually implement saving their state will be saved in a Bundle, this means that you must provide such information yourself if you want your Fragment to save some state information. Also, again: No, this does not only relate to killing the process but also to deleting Activity and Fragment objects which are not visible; like the last Activity shown -- the Process may well stay alive.
"bundles are read for resuming the process": No, the Bundle will be read to pass it to the re-construction of Activity and/or Fragment objects, there is nothing done automatically in this process (except library objects which save their state also restore their state), but Android does not "resume" the "Process" from these Bundles.
"Since the retaining of fragments is not concerned with life cycle methods": Again, I think you're mixing up the two concepts. The "retaining" of a Fragment is only performed upon configuration changes _IF_ you request it via setRetainInstance, but we're mostly talking about the re-creation of Fragment objects from a Bundle here, which does involve the life cycle methods as documented by Google.
"I would not know how to retain e.g. a pointer to a network connection": Again, this must be a statement based on your mix-up. Of course you can keep a reference to a network connection upon config change (as requested per setRetainInstance) because when that happens, everything is simply kept in memory. Also, even if your Fragment gets deleted (because it became invisible) and your process is still there (because it shows the next Activity), you can (and should) keep references to objects which are expensive to re-create, such as a network connection, in your Application object, which exists as long as your process lives (more or less). It is only when your whole app is killed by Android that you lose everything, but the serialization we're discussing happens much more often.
Your conclusion:
I concluded that they surely needed to be recreated and that the life cycle methods are therefore to be preferred over setRetainInstance(true) whenever possible. Does this assumption make any sense?
Unfortunately not, since you are mixing up completely independent concepts.
I'll give it a final try:
You will want to keep a network connection reference which you need throughout your app in your Application object because it would be a lousy user experience if you created it from scratch on a regular basis throughout your app.
Your Application object will only die if Android kills your app.
Your Activity and Fragment objects will be deleted from your app regularly when the user moves forward within your app.
When the user presses "back", Android will re-create Activity and Fragment objects from Bundles using lifecycle methods. Saving something in a Bundle makes sense if you have expensive computations to do to re-create the internal state. You can live without the Bundle mechanism because Android will always save the Intent so if you don't do anything then you'll start without saved state.
When a configuration change occurs, Android lets you optimize user experience by keeping objects in memory across the config change. Here, Activity life cycle methods get involvwed and it's up to your implementation to use the saved data effectively. For Fragments, this is where setRetainInstance' comes into play: YourFragment` will survive the config change in memory if you set it.
I have a single activity app that uses a PagerAdapter. In the OnCreate event I trigger the inflation of all 7 of the pages in the PagerAdapter. The various pages have standard widgets, one page has a google Map. It all works well when testing in the AVD.
But what if my app is paused or stopped and then restarted or resumed? Does the inflation of all my pages in the pageradapter get deleted? Do I have to reinflate all the pages again?
More generally...
I've read a lot of articles about what happens with my app gets stopped or paused and what I should do upon restart or resume but I haven't seen a precise accounting of what needs to be rebuilt and what does not need to be rebuilt. I read that "this is a great place to refresh the UI" but I'm not sure exactly what that means.
Begging questions might be...
is my internal state restored i.e. values of my variables, sqlLite db, file contents.
are the values in my widgets restored i.e. characters in a textbox.
How can I prevent corruption if the pause or stop can happen in the middle of a for loop or a code block?
Thanks,
Gary
But what if my app is paused or stopped and then restarted or resumed?
Does the inflation of all my pages in the pageradapter get deleted? Do
I have to reinflate all the pages again?
If by app you mean Activity, when it is paused this just means it is not the focused Activity right now. All the state is still there. This basically means that you cannot accept any foreground events until onResume(). If the OS decides to call, onSaveInstanceState(), you can can actually store things like values of instance variables or just flags for a new instance to read. You can't store complex things here though. So things like Thread or Cursor instances will not be appropriate. Basically anything that is not "data."
In the case where the activity is destroyed or even the process killed, then yes you will need to rebind and reinflate everything. However the beauty of this, in most cases you don't have to do anything special. Only in the cases that you may have written to a bundle in onSaveInstanceState() will you have to do some extra work.
is my internal state restored i.e. values of my variables, sqlLite db, file contents.
If the transition was only between onPause and onResume then yes. Everything should be fine. If there was a destroy or process kill, the activity will restart with onCreate() and have to reset all the state based on the Bundle savedInstanceState. I will address sqlite and files later down.
are the values in my widgets restored i.e. characters in a textbox.
Usually if they have saveEnabled(true) (most do) and for TextView there is a freezesText property that will make them remember the last text set on them. However, most of the time if you save your state correctly during the onSaveInstanceState() call, it's probable that you are storing the state not just for UI but other means as well. In which case you might as well sync them when you go through the next onCreate()
How can I prevent corruption if the pause or stop can happen in the
middle of a for loop or a code block?
So onPause() and onStop() occur on the main UI thread. If you are currently running code on the UI thread, it must complete before these other callbacks can occur. If you are running on some other thread, then yes you have to try to make outputs of task complete on the UI thread. It simplifies a lot of this.
Kills on the other hand, can happen. This usually affects things outside your program's memory though when we talk about corruption. Like if you had a file handle open or a sqlite cursor open, or a network socket open. With that, you sort of have to check the state before you use it.
i.e
Make sure directories really exist before you read or write to them
Make sure the contents of files were properly written to previously (simple expected check-sums usually work here or version metadata).
Use transactions when using sqlite to make sure you only write data to your table(s) in complete atomic chunks so that you don't have tables with rows referencing state in other tables that is not there.
Network connections will be reset, and things like your network protocol of choice should take care of sanity checking for you, along with the application being connected with.
hopefully this helps, while it's not fully complete, it's a good starting approach.
In status Pause and Stop all your objects are preserved and you don't need to worry about interruption in a loop, unless it takes more than 5 seconds to complete which may generate ANR's.
However, is good practice when entering these status to realease the objects that are not needed or can be easly recreated, specially if they are using big amount of memory, and recreate them when reentring the status Resume or Restart.
When your appliaction goes in Destroy status, then all your objects will be lost.
I have an application which only supports portrait mode. I'm passing all my arguments using serialization, passing by means of intents - intent.putExtra() ant then in onCreate() - getIntent().getExtras().getX(MY_PARAM_NAME)...
This works even when the system shuts down the VM, because of crashes related to other things. The activities seem to be started again with the correct parameters thanks to serialization.
So the question is, is save instance state necessary in my case? It seems to work well without it... didn't get any problems yet. But maybe I'm missing something, or didn't test enough.
As you've pointed out, if your Activity gets killed off (i.e. due to low resources), when it is recreated, it is passed the original Intent that started it. In your case, that means you get your serialized objects back.
Overriding onSaveInstanceState is important for the scenario where something has changed during the execution of your Activity (that hasn't been persisted elsewhere) that you would like to maintain in case it gets killed off.
For example, storing member variables in your Activity is dangerous for when the Activity is killed and recreated, unless you store them in the Bundle in onSaveInstanceState, and then restore them from the Bundle passed to onCreate.
Update: A great way to test the need for implementing that method is to force Android to kill your activities as soon as you leave them. Then, run your app and see if there are any problems. You can do this with the Dev Tools App on an emulator, or in ICS by going to Settings -> Developer options, and checking "Don't keep activities".
onSaveInstanceState() and onRestoreInstanceState() are only explicitly called by Android when the Activity needs to be recreated, generally after a configuration change (ex. changing orientation).
Taking photos using system provided component(MediaStore.ACTION_IMAGE_CAPTURE) is quit common.
As Ive experimented, with a certain rate the android system will kill the snapshot calling Activity to prevent memory related exception, and the calling activity will be created again where returned. Thus I have to save the states of the calling Activity via onSaveInstanceState, and retrieve them via onRestoreInstanceState. (If Im not correct and there is further info, please point it out)
However, I also found out that, when the killing occurs, all my information stored in the RAM were ERASED, RESETED, for example those Singleton class type objects, or static classes and their fields!
This mechanism is so frustrating, how to handle such situation??
I`ve found it out...
Some android OS kill the snapshot calling Activity to avoid memory related exception. So, I have to save all the states via method onSaveInstanceState, and retrieve them when the calling activity was constructed again.
Further more, I also found out that, all the information stored in the memory is prone to be erased, like those Singleton objects. Thus I have to do saving by some persistent storage approaches, and restore them later.