Using `onRetainCustomNonConfigurationInstance` to retain data across configuration changes - android

I've been programming for Android for some time, and I'm still looking for solutions to retain data over configuration changes. Aside from saving Parcelables to Activity's Bundle in onSaveInstanceState docs are suggesting using Fragment with setRetainInstance flag set to true.
But I've just come across some code that uses onRetainCustomNonConfigurationInstance to hold arbitrary objects (in a fancy way, but essentially big objects without references to Activity etc.). I have never seen this method used, so I have some doubts:
Is this method safe to call to store arbitrary objects (in a sense that I can be pretty sure it's gonna get called, and that it won't be deprecated/removed anytime soon)?
How is this method different from onRetainNonConfigurationInstance(), which also should return Object, and in essence should work similarly?
Is using retained fragment still better, for some reason?
As a bonus, I would be grateful for any other tips or solutions to save state of objects like AsyncTask, Observable, view's presenters and go on

Is this method safe to call to store arbitrary objects (in a sense
that I can be pretty sure it's gonna get called, and that it won't be
deprecated/removed anytime soon)?
onRetainCustomNonConfigurationInstance() is a relatively new method and it is not deprecated. I would really assume it is not going to disappear soon, because there is no reason for introducing something new just to remove it. You can use it safely.
How is this method different from onRetainNonConfigurationInstance(),
which also should return Object, and in essence should work similarly?
onRetainNonConfigurationInstance() always return an instance of inner NonConfigurationInstances class with retained fragments, loaders etc states. You cannot (and should not) change this system behavior. That's why the method is final and you cannot override it.
If you want to retain your custom instance, you need to override onRetainCustomNonConfigurationInstance() and return it from there.
In fact, onRetainNonConfigurationInstance() calls onRetainCustomNonConfigurationInstance() and retains retuned instance with the other states like retained fragments and loaders.
Is using retained fragment still better, for some reason?
It is rather a matter of your use case and preferences. The logic might be like this. If your activity just controls fragments and has no other special logic in it, then it is easier to use retained fragments. If your activity has something to retain, then you can safely use onRetainCustomNonConfigurationInstance() method. As for now, in both cases the state still gets retained by good old and deprecated onRetainNonConfigurationInstance() method.
p.s. Regarding the bonus question about storing a state I would rather suggest to look at onSaveInstanceState() method. It was intended for storing states.
Update: AndroidX release from November 5, 2018 deprecated the method with the following note: onRetainCustomNonConfigurationInstance has been deprecated. Use a ViewModel for storing objects that need to survive configuration changes.

Related

Best way to keep object reference in Activity through screen orientation changes

The only way that I see is to use a static variable, but it's not okay as I want to keep the ability to start multiple Activites.
In my case, I want to keep a reference of Dagger 2 Component.
It will provide objects like Navigator. I can't just store it into Bundle.
For now, I can't use Retain MainFragment instead of MainActivity because of this bug.(I'm at 23 API level)
Is there any Retain Activity implementation?
Huh, well you have two built in solutions in android to fix this problem:
1) If the object is serializeable or parcelable you can override onSaveInstanceState() and read the value out in onCreate. Here are the google developer docs for the specifics on that.
2) If your object is not serializable you can instead override onRetainNonConfigurationInstance() and return that object. You can then get the object back in onCreate. Look at this SO post for how to use this approach (don't persist your activity though! persist the object.). The drawback of this is that you can only persist one object at a time.
I have a more in depth write up of these two approaches and third, declarative approach that you can roll. It relies on onRetainNonConfigurationInstance and allows you to use annotations to declare what variables in your activity should be persisted. check it out here. That said, I wouldn't recommend using this unless you have more than one non-serializeable/parcelable object to persist.
Edit: clarified point that you shouldn't persist your whole activity using onRetainNonConfigurationInstance().

Understanding of onRetainCustomNonConfigurationInstance [duplicate]

I've been programming for Android for some time, and I'm still looking for solutions to retain data over configuration changes. Aside from saving Parcelables to Activity's Bundle in onSaveInstanceState docs are suggesting using Fragment with setRetainInstance flag set to true.
But I've just come across some code that uses onRetainCustomNonConfigurationInstance to hold arbitrary objects (in a fancy way, but essentially big objects without references to Activity etc.). I have never seen this method used, so I have some doubts:
Is this method safe to call to store arbitrary objects (in a sense that I can be pretty sure it's gonna get called, and that it won't be deprecated/removed anytime soon)?
How is this method different from onRetainNonConfigurationInstance(), which also should return Object, and in essence should work similarly?
Is using retained fragment still better, for some reason?
As a bonus, I would be grateful for any other tips or solutions to save state of objects like AsyncTask, Observable, view's presenters and go on
Is this method safe to call to store arbitrary objects (in a sense
that I can be pretty sure it's gonna get called, and that it won't be
deprecated/removed anytime soon)?
onRetainCustomNonConfigurationInstance() is a relatively new method and it is not deprecated. I would really assume it is not going to disappear soon, because there is no reason for introducing something new just to remove it. You can use it safely.
How is this method different from onRetainNonConfigurationInstance(),
which also should return Object, and in essence should work similarly?
onRetainNonConfigurationInstance() always return an instance of inner NonConfigurationInstances class with retained fragments, loaders etc states. You cannot (and should not) change this system behavior. That's why the method is final and you cannot override it.
If you want to retain your custom instance, you need to override onRetainCustomNonConfigurationInstance() and return it from there.
In fact, onRetainNonConfigurationInstance() calls onRetainCustomNonConfigurationInstance() and retains retuned instance with the other states like retained fragments and loaders.
Is using retained fragment still better, for some reason?
It is rather a matter of your use case and preferences. The logic might be like this. If your activity just controls fragments and has no other special logic in it, then it is easier to use retained fragments. If your activity has something to retain, then you can safely use onRetainCustomNonConfigurationInstance() method. As for now, in both cases the state still gets retained by good old and deprecated onRetainNonConfigurationInstance() method.
p.s. Regarding the bonus question about storing a state I would rather suggest to look at onSaveInstanceState() method. It was intended for storing states.
Update: AndroidX release from November 5, 2018 deprecated the method with the following note: onRetainCustomNonConfigurationInstance has been deprecated. Use a ViewModel for storing objects that need to survive configuration changes.

What are good cases not to use setReatinInstance(true) in a fragment?

Since orientation changes happen fairly quickly one would think that keeping a Fragment in memory during that time would be more efficient than recreating it again.
Since it's kept for a short time only, there seem to be little impact on memory.
What then shall be good reasons NOT to use setRetainInstance(true) for each and every Fragment?
What then shall be good reasons NOT to use setRetainInstance(true) for each and every Fragment?
Google's primary concern is that you'll screw up and have data members in the fragment that refer to the old activity that you do not clean up in post-configuration lifecycle method calls (e.g., onCreateView()). For example, you might hold onto a widget in a data member, where you do not immediately null out or repopulate that data member on a configuration change. If your fragment has a reference back to the old activity, the old activity (and everything it holds onto) cannot be garbage-collected until your fragment gets destroyed. This is one of the reasons why Google does not recommend retaining any fragment with a UI.
Firstly maybe check this link: http://android-er.blogspot.com/2013/05/how-setretaininstancetrue-affect.html
Basically you shouldn't always retain the instance of the fragment because fragments are attached to activities. Hence when an activity is re-created (i.e. on configuration change), the fragment needs to be re-associated to the new activity (which leads to extra coding and some extra problems). If you just unnecessarily setRetainInstance(true), you are giving yourself more error checking and coding to do for no reason. By setting the setRetainInstance(true), you will need to deal with a different fragment lifecycle as well because certain methods in the lifecyle are now skipped (i.e. the onCreate() is no longer called after configuration changes). As far as I understand, setRetainInstance(true), won't make it more efficient because you could use onSaveInstance to save any data that you would want to use in the recreation of the same kind of fragment.
I hope this helps.

setRetainInstance fragment with UI Android

Ok, I created a Fragment with some UI (couple textboxes and stuff) and I used setRetainInstance since Im running an AsyncTask to query a server (request can only be sent once) and I need the result of the AsyncTask. So my question is:
Is it wrong to retain the whole fragment with the UI? I saw couple examples where people use an extra Fragment to use the setRetainInstance but.. is there anything wrong not using that extra one??
If there is an issue with using the setRetainInstance why is that? Couldn't find any info in the documentation regarding this.
Even if you use setRetainInstance(true), your Fragment will still recreate its views when you rotate (you will get a call to onDestroyView and then onCreateView). As long as you don't keep references to views past onDestroyView, there will not be any leaks of the old Activity. The best approach would be to explicitly null the references in onDestroyView, but your code in onCreateView would generally overwrite those references anyway.
There are many examples online (including some official ones) where people use a separate fragment (without a view) to retain data. Assuming what I said above is correct, then this is unnecessary (for the sake of preventing leaks). In many cases, you may end up with cleaner code/architecture if you use a separate fragment whose responsibility is just to handle the data and not worry about the UI.
You can check to see if you are leaking Activity contexts after rotating by using Eclipse MAT.
If you are locking your orientation then you should be fine. Otherwise you can end up with memory leaks if you retain widgets that are associated with a particular activity instance.

Android DialogFragment and configuration changes

I'm having some trouble understanding how to make a simple DialogFragment to edit a (complex) object, say a Person, with first and last name, and a list of e-mail addresses each consisting of an enum (Work, Home, etc) and the address.
First of all, how do I properly pass the Person object to a DialogFragment? My current solution has a setPerson(Person person) method, that's called after my DialogFragment is created, but before dialog.show(). This works ok, until a configuration change happens (user rotates the screen). The DialogFragment gets recreated and the reference to my Person object is null. I know I can save the instance using onSaveInstanceState, but the object is complex and expensive, and persisting a large object this way seems wasteful.
I've also tried disabling configuration change in the activity that uses my dialog, and that fixes the problem, but I want the dialog to be reuseable and requiring all the activities that use it to disable configuration changes seems wrong.
Third option would be to save the reference to Person in a static variable, but again, I want the dialog to be reuseable and able to support multiple instances.
How do other people handle their expensive and complex objects in reuseable dialogs?
Well, there are several solutions, none of which are fantastic or failsafe if you are completely unable to serialize the object you're editing.
I don't recommend ever using android:configChanges="orientation" unless it's absolutely, 100% unavoidable. There are other configuration changes, and your app will still break with the others if you resort to using that solution.
But a simple solution that will work in the vast majority of cases is to call setRetainInstance(true) on the DialogFragment. This will prevent your Fragment from being destroyed and re-created on a configuration change. There is an edge-case where this might not work, though. There are other reasons besides configuration changes where the OS will attempt to put an activity or app 'on ice', for example to save memory. In this case, your object will be lost.
The cleanest way to pass a complicated Object to a fragment is to make the Object implement Parcelable, add the object to a Bundle, and pass the bundle to the Fragment with fragment.setArguments(bundle). You can unpack the Object in onActivityCreated() of the fragment by retrieving the bundle through a call to getArguments().
To persist the argument on configuration changes, simply save the "working" parcelable Object to the bundle provided by onSaveInstanceState(Bundle state) method of the fragment, and unpack the argument later in onActivityCreated() if savedInstanceState !=null.
If there is a noticeable performance hit from implementing Parcelable, or you have a "live" object of some kind, one option is to create a non-UI fragment to hold the data object. Without getting into details, you can setRetainInstance(true) on the non-UI fragment and coordinate the connection with the UI fragment through interfaces in the Activity.

Categories

Resources