Reputation: 1280
I facing a bit of trouble when using AsyncTaskLoader
and the screen orientation changes. Let me give you some context on how my app is structured: I have a very simple app which fetches results from a url and displays it. The app consists of one FragmentActivity
and three Fragments
. My three fragment are as follows:
The AsyncTaskLoader
is used to load data from either a content provider (if it is cached) or from the network.
Everything works great until the screen oreintaion changes! I looked at the output of LoaderManager.enableDebugLogging(true)
and it seems the source of the problem is that LoaderCallbacks.onLoadFinished
doesn't get called in my activity.
Here is a piece of the code that launches the loader:
/* This function is defined in the "first" fragment in an interface and
* implemented in the activity */
@Override
public void onFormSubmission(String word) {
showLoadingFragment();
if ( mWord == null || mWord.equals(word) ) {
getSupportLoaderManager().initLoader(0, word, this);
} else {
getSupportLoaderManager().restartLoader(0, word, this);
}
// Store the word
mWord = word;
}
The code that displays the result:
@Override
public void onLoadFinished(Loader<Bundle> loader, Bundle data) {
// Validation and other stuff
ResultFragment fragment = new ResultFragment();
// Add the fragment to the 'result_fragment_container' FrameLayout
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
// Replace loading fragment with the result
.replace(R.id.result_fragment_container, fragment)
.commitAllowingStateLoss();
}
Approaches I tried but failed:
setRetaininstance(true)
in fragments.android:configChanges="orientation|keyboardHidden|keyboard"
in the manifest.I'm out of ideas, so any help would be appreciated.
Upvotes: 8
Views: 1478
Reputation: 14438
Hi I can suggest 2 possible solutions:
1) From your question I'm not entirely sure what process your performing which is never calling OnLoadFinished() in the AsyncTask. I'll assume is a Async
DB call or Web server call etc?
Regardless the onLoadFinished() probably doesn't get called because your Activity is Destroyed on rotation. you need to save the state you updated before rotation and onDestroy()
is called
You can try keep a class variable status flag. This is set in your "update" Async
task which flags whether your Async
task is still running ie "updating" or not.
So:
bUpdating = true
when your update async
task starts. Ie within
onPreExecute()
bUpdating = false
when its finished. Ie within
onPostExecute()
Then when onDestroy()
is called (always called before the screen rotate) you could check bUpdating
. IF
TRUE
then you could save all details on screen, which would have been saved in the Async
task, to SharePreferences
Within onResume()
check your specific SharePreferences
variables and if they exist then kickoff the Async
update again.
So in short this will:
SharedPreferences
BEFORE screen rotation - onDestroy()
- if boolean says Async task running.onResume()
AFTER screen rotation check SharedPreferences
and save details via Async
.onLoadFinished()
method is called.There is probably a more reliable way also to check for Screen Rotation, i haven't done much with it.
2) Of course another alternative is to actually block screen rotation entirely on your particular Activity (because really, do you NEED rotation on that screen?).
I have an application which records video in background and uploads 10second segments. It was causing large headaches with screen rotation and my Async tasks, so I blocked rotation entirely as it was completely unnecessary. Here is code for the AndroidManifest.xml file to lock Screen orientation as Portrait:
<activity
android:name="com.blah.YourActivity"
android:label="Register"
android:screenOrientation="portrait">
</activity>
I hope that can help you out
Edit: Also, could be better more categorical ways to know that screen is rotating:
ie this sort of code:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.e(TAG, "ORIENTATION_LANDSCAPE");
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Log.e(TAG, "ORIENTATION_PORTRAIT");
}
}
Example taken from this post
you could just put your SharePreferences
save in there. but onResume() will still be used to grab your details back from.
Also a link to SharePreferences
incase you've not used it before
Edit2!: One other thing. I just re-read your question, and you mention "Loader" for some reason i had it in my head this was saving data, not loading. Have you tried simply re-calling AsyncTaskLoader()
when onResume() is called?? If your not saving then you can ignore everything i said about retaining your data to SharePreferences
because you can just kickoff the load Async task again at onResume() (which runs after Rotation)
Upvotes: 1
Reputation: 5696
You should put getSupportLoaderManager().initLoader(0, null, this);
in the onActivityCreated
method. The initLoader will create or reconnect with a loader if it already exists
Upvotes: 1