consider this example from the android tutorial,
getLoaderManager().initLoader(0, null, this);
is called onActivityCreated, giving the loaderCursorLoaderListFragment.this instance, as far as i can tell there is no garuantee that the given fragment lifecycle is equal to the Activity hence LoaderManager lifecycle. So, if the fragment is removed while Activity is still living would it not cause a memory leak?
If yes, then how should they correct this example?
Loaders are destroyed and cleaned up when the bound Fragment or Activity destroys. i.e. If you pass Fragment instance to the initLoader(), then the Loader is destroyed when that Fragment instance is destroyed.
Reference:
https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832#.ai7whgsv4
Loaders don’t stay around forever. They’ll be automatically cleaned up
when the requesting Activity or Fragment is permanently destroyed.
That means no lingering, unnecessary loads.
Related
I find Fragment#setRetainInstance(true) confusing. Here is the Javadoc, extracted from the Android Developer API:
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Question: How do you as a developer use this, and why does it make things easier?
How do you as a developer use this
Call setRetainInstance(true). I typically do that in onCreateView() or onActivityCreated(), where I use it.
and why does it make things easier?
It tends to be simpler than onRetainNonConfigurationInstance() for handling the retention of data across configuration changes (e.g., rotating the device from portrait to landscape). Non-retained fragments are destroyed and recreated on the configuration change; retained fragments are not. Hence, any data held by those retained fragments is available to the post-configuration-change activity.
It's very helpful in keeping long running resources open such as sockets. Have a UI-less fragment that holds references to bluetooth sockets and you won't have to worry about reconnecting them when the user flips the phone.
It's also handy in keeping references to resources that take a long time to load like bitmaps or server data. Load it once, keep it in a retained fragment, and when the activity is reloaded it's still there and you don't have to rebuild it.
Added this answer very late, but I thought it would make things clearer. Say after me. When setRetainInstance is:
FALSE
Fragment gets re-created on config change. NEW INSTANCE is created.
ALL lifecycle methods are called on config change, including onCreate() and onDestroy().
TRUE
Fragment does not get re-created on config change. SAME INSTANCE is used.
All lifecycle methods are called on config change, APART FROM onCreate() and onDestroy().
Retaining an instance will not work when added to the backstack.
Don't forget that the above applies to DialogFragments as well as Fragments.
The setRetainInstance(boolean) method is deprecated, use ViewModels instead.
The setRetainInstance(boolean) method on Fragments has been deprecated as of Version 1.3.0 of fragment API.
With the introduction of ViewModels, developers have a specific API for retaining state that can be associated with Activities, Fragments, and Navigation graphs. This allows developers to use a normal, not retained Fragment and keep the specific state they want retained separate.
This ensures that developers have a much more understandable lifecycle for those Fragments (one that matches all of the rest of their Fragments) while maintaining the useful properties of a single creation and single destruction (in this case, the constructor of the ViewModel and the onCleared() callback from the ViewModel).
According to Android Developer site, the correct way of communicating an activity with their fragment is through listeners.
https://developer.android.com/training/basics/fragments/communicating
My question is, this fragment is holding a reference to the activity... when the activity is destroyed, will the fragment manager release the fragment and thus the fragment will be collected and so the activity? or do they hold a strong reference that needs to be nullified too in the Fragment's onDestroy?
The Fragments Lifecycle is bound to the one of the Activity. Imagine an Activity as the Universe and Fragments as Planets / Stars. If the Universe dies, so do the Stars / Planets inside of it. Similarly, if an Activity gets destroyed so do all of it's Fragments.
The official documentation (which you should definitely check out) explains it very well:
A fragment must always be hosted in an activity and the fragment's
lifecycle is directly affected by the host activity's lifecycle. For
example, when the activity is paused, so are all fragments in it, and
when the activity is destroyed, so are all fragments. However, while
an activity is running (it is in the resumed lifecycle state), you can
manipulate each fragment independently, such as add or remove them.
When the activity containing the fragment is destroyed, so is the fragment automatically. Check this out
I don't completely understand how an activity and its fragments are recreated.
Imagine the following scenario: you have a database, activity reading this DB and a fragment, which will be created, if the activity's query to DB has some data. If DB is empty a fragment's onCreateView will cause an exception.
Now imagine we moved the app to background for a while and activity was destroyed. Moreover, the DB changes and gets empty.
How this structure will be recreated? Activity starts and understands that fragment is not needed, so everything is ok, or the fragment is recreated then anyway, because it already was created? Does setRetainInstance(true/false) has an influence on this process?
Making it more complex: we have several fragments, where one decides, if the other should be created or not in a similar way. What is the order of fragments recreation? Is it some kind of a race condition or the order is predefined?
Finally, how can we prevent or control the recreation of a fragment? I guess I have a situation, when sometimes a fragment is recreated not in right time. How such an architecture could be fixed? Simply removing fragment with onStop()?
I don't completely understand how an activity and its fragments are
recreated.
First of all it's very important to have very clear how the lifecycle of the Activity/Fragments works in Android. Here I link what is IMHO the most complete diagram on the subject (even more than the official one):
http://staticfree.info/~steve/complete_android_fragment_lifecycle.svg
Imagine the following scenario: you have a database, activity reading
this DB and a fragment, which will be created, if the activity's query
to DB has some data. If DB is empty a fragment's onCreateView will
cause an exception. Now imagine we moved the app to background for a
while and activity was destroyed. Moreover, the DB changes and gets
empty.
This kind of operation, like reading or writing to a DB (or connecting to a web service or more) are considered I/O and should get managed properly in background. I don't know concrete details in your case but in your example, you can use a CursorLoader. Android docs says the following:
A loader that queries the ContentResolver and returns a Cursor. This
class implements the Loader protocol in a standard way for querying
cursors, building on AsyncTaskLoader to perform the cursor query on a
background thread so that it does not block the application's UI.
Moreover
If DB is empty a fragment's onCreateView will cause an exception.
If a DB is empty the fragment should not cause an exception (any), if you're using, for example, a CursorLoader it will eventually get a Cursor with 0 entries. Later on (and again this depends on your implementation), if you update your database you can do a new CursorLoader petition to get a refreshed Cursor with the new data (the same applies if you rotate your device).
Two good tutorials that talks more in deep about CursorLoader are How to use Loarders written by Wolfram Rittmeyer and Life before Loaders by Alex Lockwood
Activity starts and understands that fragment is not needed, so
everything is ok, or the fragment is recreated then anyway, because it
already was created? Does setRetainInstance(true/false) has an
influence on this process?
An activity is a "dumb" component, by itself it does not know nothing about what a fragment is (well that's not quite true because the implementation of the activity knows about fragments). But for good sake it really depends on what you try to do. If you have a fragment in your activity it will be eventually recreated. Two classes you have to keep an eye to understand how this work is:
FragmentActivity.java
FragmentManagerImpl.java
Finally, how can we prevent or control the recreation of a fragment? I
guess I have a situation, when sometimes a fragment is recreated not
in right time. How such an architecture could be fixed? Simply
removing fragment with onStop()?
Never mess with lifecycle events by yourself, this is managed properly by the android runtime.
The fragment's lifecycle is tied to that of the Activity. From the Android docs:
Stopped
The fragment is not visible. Either the host activity has been stopped or the fragment has been removed from the activity but added to the back stack. A stopped fragment is still alive (all state and member information is retained by the system). However, it is no longer visible to the user and will be killed if the activity is killed.
When the Activity is destroyed, it will call the onCreate() method to recreate the Activity. This means that if the underlying database has changed, the Activity can decide not to launch the fragment.
You can read more about the Fragment lifecycle here, and the Activity lifecycle here.
I'm currently having issues with fragment lifecycle management.
Should the activity the fragment is hosted in be recreated I have set SetRetainInstanceState(true) to keep the fragment instance alive.
However, this had lead to some strange behaviour regarding my views. Sometimes I get memory leak warnings concerning a few fragment views and nullpointer exceptions to the activity context.
Wanting to make sure the fragment instance is retained properly: what are best practices regarding the retaining of a fragment (what to keep, what to destroy)?
SetRetainInstanceState(true) makes sure Android retains the fragment while the activity is being recreated. Therefore the activity the fragment was first attached to is not longer there after activity recreation and the fragment is attached to a new activity instance.
To make sure this goes well keep the following things in mind:
Do not keep a reference to the attached activity in your fragment unless absolutely necessary. Use the getActivity() method instead which will always return the currently attached fragment (or null if nothing is attached).
If you absolutely have to have a "permanent reference" to the currently attached activity (in which you might want to rethink your design) make sure to update this reference in the onAttach and onDetach methods.
Make sure you retain no object that was initialized using the activity as a context (usually views, adapters and such). To do this, override the Fragments onDestroyView() method that gets called just before activity recreation. Here you can dispose of the views and adapters the fragment still has a active reference to (usually just setting their reference to null should be enough). You can then recreate the fragment's views and adapters using the new context in the onCreateView call.
I find Fragment#setRetainInstance(true) confusing. Here is the Javadoc, extracted from the Android Developer API:
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Question: How do you as a developer use this, and why does it make things easier?
How do you as a developer use this
Call setRetainInstance(true). I typically do that in onCreateView() or onActivityCreated(), where I use it.
and why does it make things easier?
It tends to be simpler than onRetainNonConfigurationInstance() for handling the retention of data across configuration changes (e.g., rotating the device from portrait to landscape). Non-retained fragments are destroyed and recreated on the configuration change; retained fragments are not. Hence, any data held by those retained fragments is available to the post-configuration-change activity.
It's very helpful in keeping long running resources open such as sockets. Have a UI-less fragment that holds references to bluetooth sockets and you won't have to worry about reconnecting them when the user flips the phone.
It's also handy in keeping references to resources that take a long time to load like bitmaps or server data. Load it once, keep it in a retained fragment, and when the activity is reloaded it's still there and you don't have to rebuild it.
Added this answer very late, but I thought it would make things clearer. Say after me. When setRetainInstance is:
FALSE
Fragment gets re-created on config change. NEW INSTANCE is created.
ALL lifecycle methods are called on config change, including onCreate() and onDestroy().
TRUE
Fragment does not get re-created on config change. SAME INSTANCE is used.
All lifecycle methods are called on config change, APART FROM onCreate() and onDestroy().
Retaining an instance will not work when added to the backstack.
Don't forget that the above applies to DialogFragments as well as Fragments.
The setRetainInstance(boolean) method is deprecated, use ViewModels instead.
The setRetainInstance(boolean) method on Fragments has been deprecated as of Version 1.3.0 of fragment API.
With the introduction of ViewModels, developers have a specific API for retaining state that can be associated with Activities, Fragments, and Navigation graphs. This allows developers to use a normal, not retained Fragment and keep the specific state they want retained separate.
This ensures that developers have a much more understandable lifecycle for those Fragments (one that matches all of the rest of their Fragments) while maintaining the useful properties of a single creation and single destruction (in this case, the constructor of the ViewModel and the onCleared() callback from the ViewModel).