Reputation: 15744
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?
Upvotes: 3
Views: 1734
Reputation: 2679
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.
Upvotes: 1
Reputation: 1783
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.
Upvotes: 0
Reputation: 12345
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.
Upvotes: 3
Reputation: 716
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.
Upvotes: 1