According to http://developer.android.com/guide/components/loaders.html, one of the nice thing about loader is that, it is able to retain its data during configuration chang
If you are using FragmentManager's replace fragment technique this issue will happen.
When you replace/remove the Fragment, the fragment is detached from the activity and since loaders are attached to the activity, the loaders will be recreated during orientation change.
Try using FragmentManager's hide/show technique. May be this will help you.
My answer is quite straight forward actually. Don't use AsyncTaskLoaders. Because a few bugs regarding AsyncTaskLoaders you knew it by now.
A good combination would be a retainable (setRetainInstance(true) in onActivityCreated()) fragment with AsyncTask. Works the same way. Just have to restructure the code a bit.
Although the author doesn't provide any code example, this is the closest workable solution. I do not use the author proposed solution. Instead, I still rely on AsyncTaskLoader
for all the necessary loading task. The workaround is that, I will rely on an additional retained fragment, to determine whether I should reconnect/create loader. The is the skeleton code on the whole idea. Works pretty well so far as long as I can tell.
@Override
public void onActivityCreated(Bundle savedInstanceState) {
...
dataRetainedFragment = (DataRetainedFragment)fm.findFragmentByTag(DATE_RETAINED_FRAGMENT);
// dataRetainedFragment can be null still...
}
@Override
public void onResume() {
...
if (this.data == null) {
if (dataRetainedFragment != null) {
// Re-use!
onLoadFinished(null, dataRetainedFragment);
} else {
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
} else {
}
}
@Override
public void onLoadFinished(Loader<Data> arg0, Data data) {
this.data = data;
if (this.dataRetainedFragment == null) {
this.dataRetainedFragment = DataRetainedFragment.newInstance(this.data);
FragmentManager fm = getFragmentManager();
fm.beginTransaction().add(this.dataRetainedFragment, DATE_RETAINED_FRAGMENT).commitAllowingStateLoss();
}
Try to change,
@Override
public void onResume()
{
super.onResume();
if (result == null) {
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
} else {
// Restore from previous state. Perhaps through long pressed home
// button.
}
}
to
@Override
public void onResume()
{
super.onResume();
Loader loader = getLoaderManager().getLoader(0);
if ( loader != null && loader.isReset() ) {
getLoaderManager().restartLoader(0, getArguments(), this);
} else {
getLoaderManager().initLoader(0, getArguments(), this);
}
}
I've had success subclassing AsyncTaskLoader and making a few tweaks to its methods.
public class FixedAsyncTaskLoader<D> extends AsyncTaskLoader<D> {
private D result;
public FixedAsyncTaskLoader(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (result != null) {
deliverResult(result);
} else {
forceLoad();
}
}
@Override
public void deliverResult(T data) {
result = data;
if (isStarted()) {
super.deliverResult(result);
}
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
result = null;
}
@Override
protected void onStopLoading() {
cancelLoad();
}
}