Saving state on List Data when App Resumes - android

Here is my set up.
I have a Main SherlockFragmentActivity. It swaps many ListFragments back and forth with FragmentTransaction's. To indicate loading, anytime a ListFragment loads the data I call:
setSupportProgressBarIndeterminateVisibility(true);
The problem:
When the main Activity mentioned above first starts, or the user leaves and goes to other apps and then restarts this one after an extended period of time, the SherlockFragmentActivity seemingly reloads, there is no progress dialog in the ActionBar, the screen is white for a few seconds, and then the list data repairs (The length depends on the data connection).
Here is some supplemental code: When the main/base Activity first loads, this is one of the first things I do in the onCreate():
// Set up Main Screen
FragmentTransaction t2 = this.getSupportFragmentManager().beginTransaction();
SherlockListFragment mainFrag = new FollowingFragment();
t2.replace(R.id.main_frag, mainFrag);
t2.commit();
FollowingFragment is the one that will always load in this instance. It contains a ListView and an AsyncTask pulling data from a MySQL database.
My question: How do I prevent this delay? And how do I handle maintaining the data when user leaves for extended periods of time?

This is the normal behavior, it happens because your activity has been killed to save memory for other apps, when your app was in the background. And when it comes back to the foreground, the system recreate your activity, which will recreate your fragment.
But if your really want to avoid recreating your fragment, you can use setRetainInstance in your fragment's onCreate method:
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.
And use something like this in your FragmentActivity's onActivityCreated method:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Check to see if we have retained the worker fragment.
mRetainableFragment = (RetainedFragment)fm.findFragmentByTag("fragmentTag");
// If not retained (or first time running), we need to create it.
if (mRetainableFragment == null) {
mRetainableFragment = new RetainedFragment();
// Tell it who it is working with.
mRetainableFragment.setTargetFragment(this, 0);
fm.beginTransaction().add(mRetainableFragment, "fragmentTag").commit();
}
}
But be aware that, this should only be use for headless fragment (fragment without UI, i.e return null in onCreateView, aka worker fragment). You can still use this method for UI fragment though but it is not recommanded by google, in that case the data must be stored as member (field) in your activity. If the data which should be stored is supported by the Bundle class, you can use the onSaveInstanceState() method to place the data in the Bundle, and retrieve that data in the onActivityCreated() method.
Moreover this only works if the fragments is not added to the backstack.

According to the Android developer reference page on Activity, you have to request the progress bar feature before calling setSupportProgressBarIndeterminateVisibility():
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_main);
setSupportProgressBarIndeterminateVisibility(true);
The other issue, reloading the fragments, is due to Android killing your ListFragment so that they have to reload could be resolved by overriding onSaveInstanceState(Bundle outState) and caching your data there to be retrieved in your ListFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(savedInstanceState != null) {
// retrieve data from Bundle here
} else {
// no data, we need to reload from network
}
// initialize your View here
}
This method is not guaranteed to run all the time, however (it's not in the Fragment lifecycle). As such, you should also make sure you cache the data in onPause() and use it instead of always loading from a network connection.
#Override
public void onPause() {
super.onPause();
SharedPreferences prefs = getActivity().getSharedPreferences();
SharedPreferences.Editor editor = prefs.edit();
// put your data here using editor
editor.commit();
}
Then you can load this data in your onCreateView() by retrieving an instance of SharedPreferences and using prefs.getString(String key) and other methods.

When your app get killed, you lose your activity state and data! There are two scenarios that I can assume about your AsyncTask:
1. you are pulling some data from a Webserver. In this case I personally think caching your data which you retrieved from webserver is a better solution than implementing serializable.
2. You are pulling lots of data from local database (which causes retrieving data to take some time). In this scenario I suggest retrieving only as much data as you need, not more! (for example you can retrieve 20 items, and when user scrolling to the end of ListView retrieve next 20 items).
This solution helps your application retrieve data faster.
PS: To give you a clue how to implement the WebserviceModule with cache capability, which I assume is located in your AsyncTask, you can save every response from webserver in the SDCard and every time you trying to retrieve some resource from webserver, you should check the SDCard to see if your request already sent and cached! For every request, your should make a unique signature base on url and post parameters to recognize cached files.

When you return to activity after extending period of time, the whole app being restarted. So You can't rely on object variables to save data.
So You could avoid delay You've mentioned with saving data to some local storage in activity onStop() method. For example, shared preferences.
And when You call onCreate(), check whether You have data saved and use it if exists (and clean up to have "clean" start next time), otherwise start asynctask.

Related

Accessibility focus is reset in Fragments

I have a button on the screen, when I open a new Fragment by clicking on that button with TalkBack and return to the previous Fragment (fm.popBackStack()), the button should get accessability focus again. But instead, the focus is reset to the first element on the screen as on the first visit. The same with RecyclerView, when I open a new Fragment by clicking on an item, then when I return, the focus should return to the same item on which it was. It works as expected with activity, but not with fragments. How it can be fixed?
Tested on Android 9, 11 and Fragment 1.3.0, 1.4.0, 1.4.1 versions
This will be a long answer because this question has various solutions depending on your needs, and I decided to take time and be thorough because accessibility is essential!
tl;dr solution for your problem:
To correctly save the instance state of Fragment you should do the following:
In the fragment, save instance state by overriding onSaveInstanceState() and restore in onActivityCreated():
class MyFragment extends Fragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
if (savedInstanceState != null) {
//Restore the fragment's state here
}
}
...
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's state here
}
}
And important point, in the activity, you have to save the fragment's instance in onSaveInstanceState() and restore in onCreate()
class MyActivity extends Activity {
private MyFragment
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
//Restore the fragment's instance
mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
...
}
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
}
}
Deep dive - Managing UI state
You can efficiently save and restore the UI state by dividing the work among the various types of persistence mechanisms. In most cases, each of these mechanisms should store a different kind of data used in the activity, based on the tradeoffs of data complexity, access speed, and lifetime:
Local persistence: Stores all data you don't want to lose if you open and close the activity.
Example: A collection of song objects, including audio files and metadata.
ViewModel: Stores all the data needed to display the associated UI Controller in memory.
Example: The song objects of the most recent search and search query.
onSaveInstanceState(): Stores a small amount of data needed to quickly reload the activity state if the system stops and then recreates the UI Controller. Instead of storing complex objects here, persist the complex objects in local storage and store a unique ID for these objects in onSaveInstanceState()
Example: Storing the most recent search query.
As an example, consider an activity that allows you to search through your library of songs. Here's how different events should be handled:
The ViewModel immediately delegates this data locally when the user adds a song. If this newly added song should be shown in the UI, you should also update the data in the ViewModel object to reflect the addition of the song. Remember to do all database inserts off of the main thread.
When the user searches for a song, whatever complex song data you load from the database for the UI Controller should be immediately stored in the ViewModel object. You should also save the search query itself in the ViewModel object.
When the activity goes into the background, the system calls onSaveInstanceState(). You should save the search query in the onSaveInstanceState() bundle. This small amount of data is easy to save. It's also all the information you need to get the activity back into its current state.
Options for preserving UI state
When the user's expectations about UI state do not match default system
behaviour, you must save and restore the user's UI state to ensure that the
system-initiated destruction is transparent to the user.
Each of the options for preserving UI state vary along the following dimensions that impact the user experience:
ViewModel
Saved instance state
Persistent storage
Storage location
in memory
serialized to disk
on disk or network
Survives configuration change
Yes
Yes
Yes
Survives system-initiated process death
No
Yes
Yes
Survives user complete activity dismissal/onFinish()
No
No
Yes
Data limitations
complex objects are fine, but space is limited by available memory
only for primitive types and simple, small objects such as String
only limited by disk space or cost/time of retrieval from the network resource
Read/write time
quick (memory access only)
slow (requires serialization/deserialization and disk access)
slow (requires disk access or network transaction)
SavedStateRegistry
Beginning with Fragment 1.1.0, UI controllers, such as an Activity or Fragment, implement SavedStateRegistryOwner and provide a SavedStateRegistry that is bound to that controller. SavedStateRegistry allows components to hook into your UI controller's saved state to consume or contribute to it. For example, the Saved State module for ViewModel uses SavedStateRegistry to create a SavedStateHandle and provide it to your ViewModel objects. You can retrieve the SavedStateRegistry from within your UI controller by calling getSavedStateRegistry().
Components that contribute to the saved state must implement SavedStateRegistry.SavedStateProvider defines a single method called saveState(). The saveState() method allows your component to return a Bundle containing any state that should be saved from that component. SavedStateRegistry calls this method during the saving state phase of the UI controller's lifecycle.
class SearchManager implements SavedStateRegistry.SavedStateProvider {
private static String QUERY = "query";
private String query = null;
...
#NonNull
#Override
public Bundle saveState() {
Bundle bundle = new Bundle();
bundle.putString(QUERY, query);
return bundle;
}
}
To register a SavedStateProvider, call registerSavedStateProvider() on the SavedStateRegistry, passing a key to associate with the provider's data as well as the provider. The previously saved data for the provider can be retrieved from the saved state by calling consumeRestoredStateForKey() on the SavedStateRegistry, passing in the key associated with the provider's data.
Within an Activity or Fragment, you can register a SavedStateProvider in onCreate() after calling super.onCreate(). Alternatively, you can set a LifecycleObserver on a SavedStateRegistryOwner, which implements LifecycleOwner, and register the SavedStateProvider once the ON_CREATE event occurs. Using a LifecycleObserver lets you decouple the registration and retrieval of the previously saved state from the SavedStateRegistryOwner itself.
class SearchManager implements SavedStateRegistry.SavedStateProvider {
private static String PROVIDER = "search_manager";
private static String QUERY = "query";
private String query = null;
public SearchManager(SavedStateRegistryOwner registryOwner) {
registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
if (event == Lifecycle.Event.ON_CREATE) {
SavedStateRegistry registry = registryOwner.getSavedStateRegistry();
// Register this object for future calls to saveState()
registry.registerSavedStateProvider(PROVIDER, this);
// Get the previously saved state and restore it
Bundle state = registry.consumeRestoredStateForKey(PROVIDER);
// Apply the previously saved state
if (state != null) {
query = state.getString(QUERY);
}
}
});
}
#NonNull
#Override
public Bundle saveState() {
Bundle bundle = new Bundle();
bundle.putString(QUERY, query);
return bundle;
}
...
}
class SearchFragment extends Fragment {
private SearchManager searchManager = new SearchManager(this);
...
}
Use ViewModel to handle configuration changes
ViewModel is ideal for storing and managing UI-related data while actively using the application. It allows quick access to UI data and helps you avoid refetching data from network or disk across rotation, window resizing, and other commonly occurring configuration changes.
ViewModel retains the data in memory, which means it is cheaper to retrieve than data from the disk or the network. A ViewModel is associated with an activity (or some other lifecycle owner) - it stays in memory during a configuration change. The system automatically associates the ViewModel with the new activity instance that results from the configuration change.
ViewModels are automatically destroyed by the system when your user backs out of your activity or fragment or if you call finish(), which means the state will be cleared as the user expects in these scenarios.
Unlike saved instance states, ViewModels are destroyed during a system-initiated process death. This is why you should use ViewModel objects in combination with onSaveInstanceState() (or some other disk persistence), stashing identifiers in savedInstanceState to help view models reload the data after system death.
If you already have an in-memory solution in place for storing your UI state across configuration changes, you may not need to use ViewModel.
Use onSaveInstanceState() as backup to handle system-initiated process death
The onSaveInstanceState() callback stores data needed to reload the state of a UI controller, such as an activity or a fragment, if the system destroys and later recreates that controller.
Saved instance state bundles persist through configuration changes and process death but are limited by storage and speed because onSavedInstanceState() serializes data to disk. Serialization can consume a lot of memory if the serialized objects are complicated. Because this process happens on the main thread during a configuration change, long-running serialization can cause dropped frames and visual stutter.
Do not use onSavedInstanceState() to store large amounts of data, such as bitmaps or complex data structures requiring lengthy serialization or deserialization. Instead, store only primitive types and simple, small objects such as String. As such, use onSaveInstanceState() to store a minimal amount of data necessary, such as an ID, to re-create the data necessary to restore the UI back to its previous state should the other persistence mechanisms fail. Most apps should implement onSaveInstanceState() to handle system-initiated process death.
Depending on your app's use cases, you might not need to use onSaveInstanceState() at all.

Fragment state lost when device left idle

Fragment losing state and shows an empty UI if left idle for 20 minutes. I'm using FragmentStatePagerAdapter and I have tried calling the notifyDataSetChanged in onStart() of my FragmentActivity.
Kindly help me how to save the object and state of my fragment and reuse it on reload of the app.
Android can kill your app if needed, you need to use onSaveInstanceState to keep your state in this cases. (Remember: Save important data in onPause!)
onSaveInstanceState exists in Activity and Fragments and is used in the same way like an activity
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("integer", anInteger);
}
Now in onCreate, onCreateView or onActivityCreated you have this argument BundlesavedInstanceState which corrisponds to the bundle saved. (Check if it's null too.)
If not enought maybe Android killed your FragmentManager too, so you need to override onSaveInstanceState and onRetoreInstanceState in your Activity and restore the fragment.
Maybe this answer could help you about the last thing i said: Using onSaveInstanceState with fragments in backstack?
A Fragment's life-cycle is closely tied to the Activity's lifecycle. This means, when your Activity goes idle; it will kill off any contained Fragments. To store Fragments you could always retain them in concordance with the Fragment API. This means you will generally be using the Fragment in a background. However the best way to keep a from being destroyed or lost from an Activity's end would be to store relevant information in a custom object and then to recreate the Fragment when the Activity is resumed.
For instance; I could have a custom object that would store relevent UI values for my Fragment and when my Activity either idles or changes I would save those relevant values to my custom object that I created. Then, when either a new Activity is created; or my old Activity is resumed; I would retrieve those values and put them back into my Fragment's UI. Hoped this helped :)
In case android needs memory, it kills the running apps. So you must save the objects using
public void onSaveInstanceState(Bundle savedState) {}
Note that savedState must be serializable.
You must call notifyDataSetChanged() in onResume(), because it ensures that it is called when the activity resumes.
For a detailed answer, please post your code.
Hard to answer without your code.
However I can say that the state is usually saved by the savedInstanceState
Usually in the onActivityCreated you have something like the following. In the example I give I save a boolean of either text was entered or not.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mHasText = savedInstanceState.getBoolean(HAS_TEXT_TAG);
} else {
// do something
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(HAS_TEXT_TAG, mHasText);
}
it's just an example as without your code it's difficult to anwer

Should I be saving persistent data from the host activity or its fragments?

This is more of a structure/design philosophy question than anything.
I have main host activity that currently saves and loads profiles for viewing by the user.
The profiles' credentials are displayed within Fragment A (user name, birthday, etc). This is done by passing the profile as an argument to the fragment before using the fragment transaction to display it.
If the user leaves or rotates their screen, how should I go about saving that profile data? Should I do the saving from the onPause within Fragment A or its HostActivity?
And what if Fragment A also allows for profile editing? When the user confirms their changes, shall I let its HostActivity know to update the main profile being held from the activity?
Or would it be better to just wait until a FragmentA.onPause is called? I suppose I could wait until something forces the fragment to call onPause, at which point I can save both the state of the fragment as well as the profile activity from the host activity.
The main thing that confuses me is: Do I need to be managing two Profile objects? The HostActivity and its FragmentA both use it. It gets a bit confusing having to run back and forth between saving, loading, and editing. Can't I just handle all of this from one class?
Okay, I have two very good and viable answers. Which one is better for my purposes though? Should I use an sqlLite database or a global Java singleton to handle my profile? Only one profile can be active per session. Saving, loading, and editing of the profile must also be taken into consideration.
If there can only ever be one profile per user in one session (as you have suggested above) why not use a Java singleton class.
private ProfileOject() {
// Exists only to defeat instantiation.
}
public static ProfileObject getInstance() {
if(instance == null) {
instance = new ProfilerObject();
}
return instance;
}
Now use getters and setters as normal.
In each activity you can get the Profile like so:
profile = ProfileObject.getInstance();
This will mean that if you make an update in Fragment A, when the activity is called, it will fetch the updated values from the profile object.
In regards to onRotate/Pause/Resume use savedInstanceState, see example below:
//Use onSaveInstanceState(Bundle) and onRestoreInstanceState
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("MyBoolean", true);
// etc.
super.onSaveInstanceState(savedInstanceState);
}
//onRestoreInstanceState
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
}
Why don't you use a sqlite database for storing user profile data? Everytime you need access to user profile data you can just query it.
You can save all the relevant data in the onPause method. Using a database will ensure data persistence, resisting any fragment/activity recreation!

Fragment has target not in Fragment Manager

I'm new to android programming.
I just try to save the state of the ListView in my fragment. For that I follow headless fragments (fragment which has no UI). In this fragment, I save the data, used in the ListView, and starting the headless fragment from the main fragment (the one which has the UI).
Now I got the exception:
java.lang.IllegalStateException: Failure saving state: RetainedFragment{4161f850 #1 work} has target not in fragment manager: JobOpeningFramgent{41601c00}
As far my concern, this is happening when I'm trying to replace the fragments with another one in the DrawerLayout.
Please temme the cause of this exception, for better understanding.
Thanks.
Boopathy.
Here's a workaround:
put this in the fragment that causes the problems:
#Override
public void onSaveInstanceState(final Bundle outState) {
setTargetFragment(null, -1);
...
and remember to set it to the real target fragment when you need it.
I'm not sure what do you want to save and where do you want to save it.
The official docs state that: "A Fragment represents a behavior or a portion of user interface in an Activity."
Using a Fragment as a container of another Fragment's UI state is generally a bad idea.
If you want to persist some values throughout the activity lifecycle (that includes screen rotations) just override onSaveInstanceState method. If you want to store some variables even after activity life-time use singelton class or Preferences, and if you want to store your values even after app life-time use SharedPreferences
Please elaborate on what do you exacly want

ASyncTask, hidden fragments, retaining instances, and screen orientation changes

My setup is as follows.
I have a FragmentPagerAdapter called from my Activity which loads two fragments. This is setup within onCreate.
In onResume I call an ASyncTask which loads data from a database, and then calls a callback in my activity onLoadComplete via a load data listener.
#Override
public void onLoadComplete(JSONArray data) {
// TODO Auto-generated method stub
LocalFragment fragmentB = (LocalFragment)getSupportFragmentManager().findFragmentByTag(ListTag);
fragmentB.setList(data);
LMapFragment fragmentA = (LMapFragment)getSupportFragmentManager().findFragmentByTag(MapTag);
GoogleMap our_map = fragmentA.getMap();
fragmentA.plotP(myLocation,data);
}
The fragments are initialized by the Pager, and within each fragments code I set the respective tag e.g in LocalFragment
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
String myTag = getTag();
((PagerTest) activity).setListTag(myTag);
Log.d("what",myTag);
}
This allows me to access the fragment, call a function within it which populates a list or populates a map. It works.
What I am now trying to do is account for screen orientation changes.. If while the ASyncTask is running the orientation is changed, the app crashes.
As suggested here: Hidden Fragments I have been trying to implement a hidden fragment which saves the state of my ASyncTask. So what I have done is set it up so in onResume of my Activity i call a function
static LoadDataFromURL the_data = null;
static JSONArray pub_data = null;
private static final String TAG = "RetainFragment";
public RetainFragment() {}
public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
if (fragment == null) {
fragment = new RetainFragment();
fm.beginTransaction().add(fragment, TAG).commit(); // add this
}
return fragment;
}
which essentially saves my data.
Basically what this means is that if i rotate my screen i dont call my ASyncTask again.. the screen just rotates.. it works perfectly.
If however I go back to the main menu and then click on the activity again the screen returns blank (but does not crash). My understanding is that the data is retained as an object in the fragment, but on reloading the activity afresh the data needs to be set again.. I.E onLoadComplete needs to be called to populate the list/map..
So i concluded that if initially after the ASyncTask completes i save the returned data in my hidden fragment onRetainInstance, then i could simply call onLoadComplete and pass it..
The problem is, in this situation seemingly the fragment has not been called yet, as such the tags are null, and calling the callbacks within onLoadComplete crashes the app.
I have been banging my head over this for ages.
My ASyncTask is in a seperate class: LoadDataFromURL
What i want to achieve is as follows - a fragmentviewpager whereby on screen rotate the ASyncTask is retained on rotate/attached to the new activity, and if it has completed before it shouldn't be run again..
Could anyone advise.
Many Thanks
EDIT
Having changed the variables in my secret fragment to public variables, everything has seemingly come together.. BUT because im not 100% sure how/when things are called, I dont fully understand WHY it works..
So.. I call findOrCreateRetainFragment and it either creates a new 'secret' fragment or returns the current instance.
If it is returning a current instance, i dont call my async task again. If it is not, I call my asynctask and load the data.
With this setup, when i load the activity and rotate the screen, it rotates as expected woop.
Now, when i go back to the main menu and then click the activity again, it calls the async task.
My understanding is that on rotate the async task is not called again, and the viewpager is somehow saving the fragments.
On the other hand, when i go back my activity is destroyed, as is my secret fragment, and as such when i click on it again it loads the data. THis is essentially what i want..
Have i understood this correctly?
Thanks
There are a few issues here that you're experiencing (I think).
First of all, the reason your callbacks crash is because they're attached to an old Activity that no longer "exists" after a screen orientation and/or Activity push. If you use onAttach() to attach a callback to your fragment, you must use onDetach() to detach that callback when the Fragment is removed from the Activity. Then, whenever you call the callback, check for a null so you don't send data to a dead object.
Basically, the logic you're trying to use here is:
Start Activity.
Check if your Fragment exists. If it does, grab it. Else, create it.
Retrieve the data if it exists. If not, wait for the callback.
Because of the nature of callbacks (depending on your implementation), you will not receive data until the event fires. However, if the Activity is gone and the event has already fired, the callback won't execute. Thus, you have to retrieve the data manually. When using setRetainInstance(), it's helpful to think of it as this entity detatched from your Activity. It will exist as long as you don't pop the current Activity or push a new Activity. However, your current Activity will be destroyed upon screen orientation changes while the Fragment won't. As such, the Fragment shouldn't rely on the existence of the Activity.
A much more elegant solution to the problem that you may want to look in to is implementing the Android Loader API. Loaders are handy tools that are handled by the system that work is roughly the same way but are more in-tune with asynchronously retrieving data. They work effectively the same way. You simply start your loader and the system with either create one if it doesn't exist or re-use one that already exists. It will remain in the system by the LoaderManager upon configuration changes.
EDIT:
To answer your edit, I guess I'll explain what's happening. It's convoluted, so just tell me if anything needs clarification.
Fragments aren't technically speaking part of your currently running Activity. When you create an instance of the Fragment, you have to call beginTransation() and commit() on the FragmentManager. The FragmentManager is a singleton that exists within the realm of your application. The FragmentManager holds on to the instance of the Fragment for you. The FragmentManager then attaches the Fragment to your Activity (see onAttach()). The Fragment then exists within the FragmentManager which is why you never really have to hold a reference to it within your application. You can just call findFragmentByTag/Id() to retrieve it.
Under normal circumstances, when your Activity is being destroyed, the FragmentManager will detach the instance of your Fragment (see onDetach()) and just let it go. The Java garbage collect will detect that no reference to your Fragment exists and will clean it up.
When you call setRetainInstace(), you're telling the FragmentManager to hold on to it. Thus, when your Activity is being destroyed on a configuration change, the FragmentManager will hold on to the reference of your Fragment. Thus when your Activity is rebuilt, you can call findFragmentByTag/Id() to retrieve the last instance. So long as it didn't keep any context of the last Activity, there shouldn't be any problems.
Traditionally, one would use it to keep references to long standing data (as you are) or to keep connection sockets open so a phone flip doesn't delete it.
Your ViewPager has nothing to do with this. How it retrieves the Fragments is completely dependent on how you implement that Adapter that it's attached to. Usually, retained Fragments don't have Views themselves because Views hold Context data of the Activity they were created in. You would just basically want to make it a data bucket to hold on to the data for the Views to pull from when they're being inflated.

Categories

Resources