AsyncTaskLoader won't call onLoadFinished() after a rotation - android

On a button click I want to create a new SQLite database from two XML files. Using LoaderManager and AsyncTaskLoader seems to be appropriate for this.
The Fragment.onCreate() calls:
setRetainInstance(true);
When the button is clicked, my Fragment sets a flag that it is creating the database, changes the layout to show a ProgressBar and cancel button, and calls:
getLoaderManager().initLoader(0, extra, this);
The operation takes 12 seconds on my PC/Emulator. If I rotate the screen, the loader continues to call the Fragment's onLoaderProgressUpdate(int) (an interface callback method) and completes the creation of the database. But it never calls the onLoadFinished() method, where I can reset the layout, enable buttons and the action bar menu that require the database to be there.
I tried doing this in Fragment.onCreateView():
if (mCreatDBInProgress) {
mBTN_CreateDB.setEnabled(false);
mDBLayout.setVisibility(View.GONE);
mProgressBarLayout.setVisibility(View.VISIBLE);
getLoaderManager().restartLoader(0, null, this);
}
But it calls my onCreateLoader() method and starts the whole thing from scratch. This is a problem, since I have not called the method that deletes the database and goes through the process of determining what XML files to use for creating the database. The XML filenames are what I pass to that method in the Bundle arg.
I don't get why the ProgressBar callback method continues to work, but the AsyncTaskLoader can't call the onLoadFinished() method. LogCat shows that it did the deliverResult().
I tried to make the Bundle a member variable, so I could re-use it on the restartLoader() call. It start another asyncTaskLoader and my ProgressBar goes back and forth as both threads try to update it. Eventually, it gets an SQLConstraintException trying to add a row to the database that was already added by the first thread.

Well, suck me sideways! I came up with one more idea, and it worked!
Instead of calling restartoader() after the rotation, just call:
getLoaderManager().initLoader(0, mExtra, this);
For (whatever) reason, that causes the AsynTaskLoader to just continue with the first thread (instead of starting another) and relink to my Fragment for the onLoadFinished() call.
So:
initLoader() - reconnects the loader to your Fragment IF you call it with the same arguments (int, Bundle, this).

I had the same problem, when after rotating screen onLoadFinished not called.
I've seen a lot of answers but nothing really fix the issue.
The only thing that helps is to simply add call to getLoaderManager() in onCreate method (it is called again after activity is recreated because of rotation).
It's really fixed the issue for me:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoaderManager loaderMngr = getLoaderManager();
Log.d(LOG_TAG, "" + loaderMngr);
And a little note about why I didn't just placed initLoader or restartLoader here: because Loader in my case is triggered after another event happens, so this was the only way to solve this problem for me.
Originally idea got from here.

Related

Using result of a thread in OnResume()

My Problem: Is it possible to prevent an activity to call OnResume() when it is being created? As I saw after the OnCreate() and onStart() method runs, the next one is the onResume(), although I only want to have it when I resume the activity from the paused state.
Why do I need this: I launch my activity (FragmentActivity, so lets say OnPostResume() ) starting with a thread which takes about 2-3s to be ready getting data from an external database. After the thread is done, I call a method which needs these data and I want to call it everytime that activity gets visible. The thread runs only when the FragmentActivity is created (onCreate()), and I cannot put the method into the onResume() because onResume() would be running way before the thread would finish its task. So it would receive not-ready data.
Anyone has a better idea?
Not sure of the exact application of this but I'll make a suggestion.
If you use an AsyncTask, you can send it off to get the data you need and in the onPostExcecute() method you can call your method that requires the data or update the view as needed. (It runs on the UI thread)
If you happen to already have the data you need in certain scenarios you could also bypass the AsyncTask and directly update the view.
This AsyncTask can be triggered in the onResume() method.
If I'm missing something, please let me know and I can adjust my suggestion.
I didn't understand the purpose of this, but here's a possible solution:
If you only wish to get the even of onResume on states that didn't have the onCreate before, just use a flag.
In the onCreate, set it to true, in the onResume check the flag (and also set it to false). if it was true, it means the onCreate was called before.
I personally would prefer to check if the result available, rather than always executing the getter-code in onResume. If the user somehow resumes your activity before the background thread is finished, you'd have a call on onResume, but don't want to display a result.
Maybe it would be a good idea to calculate/fetch the values in the thread, and let the thread return immediately (and cause the values to get filled in) if the values are already cached somewhere. That way you'd only have one entry point (the thread) for updating your UI instead of two (the thread and the onResume method).

OnLoadFinished() called twice

I'm trying to figure out if I'm doing something wrong with respect to Loaders. I'm using the support library, and I have a Fragment which in onCreate() calls initLoader() setting itself as the LoaderCallbacks, however on a rotation it is receiving the result twice in onLoadFinished(), once as a result of calling init (and it already having the data), and once as a result of FragmentActivity looping through all Loaders in onStart() and delivering the result since it already has the data.
If I only call init once (on first launch of the Fragment), it doesn't set itself as the callback for the Loader so it doesn't receive a call to onLoadFinished at all. It seems as though onLoadFinished should only be called once since some expensive things may be done in onLoadFinished() (such as clearing list adapters, etc.), so I'm just trying to figure out if this is a bug or if I am just calling init at the wrong time or something else.
Anyone have any insight to this issue?
I had a similar problem and the cause was that I had initLoader and restartLoader in my code. Depending on user's action, my query could change, so I needed to restart my loader.
The solution was to use only restartLoader, even in onResume callback method replace initLoader with restartLoader.
This is a rather old question, but for future readers I have an alternative solution. Basically what I ended up doing was restart the loader if it existed.
public void onActivityCreated(Bundle savedInstanceState) {
...
if(getLoaderManager().getLoader(Constants.LOADER_ID) == null) {
getLoaderManager().initLoader(Constants.LOADER_ID, null, this);
} else {
getLoaderManager().restartLoader(Constants.LOADER_ID, null, this);
}
...
}
This solved my issue with that on screen rotate the loader was triggered twice. One thing too note is that this is only needed for me on Android < 6 that I tested. Android 6 seem to not have this issue at all.
I am experiencing same problem my self, with no good solution.
It seems as bug in Android framework, here is similar thread in which proposed solution is to place initLoader() in onResume() - I have tried it and it works, on onLoadFinished() gets called only once:
Android: LoaderCallbacks.OnLoadFinished called twice
See my post at Android: LoaderCallbacks.OnLoadFinished called twice
I had a similar problem when restarting Fragments in a ViewPager. My solution is to remove the Loader once I'm finished with it (at the end of onLoadFinished) by calling
getLoaderManager().destroyLoader(LOADER_ID);
Hope it helps!
It is looks like framework Loader wrong implementation/bug.
1. look at what I got from Log.v(LOG_TAG, ...) messages from every important method/callback after screen rotation:
...: .initLoader() 100
...: onStartLoading()
...: onLoadFinished()
...: updateUi(); articles size=10
...: loadInBackground()
...: getInputStream() - HttpRequest
...: onLoadFinished()
...: updateUi(); articles size=10
2. As you can see everything after 'updateUi()' is no needed there.

Execute an AsyncTask after Activity is shown

I'm developing an Android 3.1 application.
I want to execute an AsyncTask after activity is shown. I want to show something to user before execute AsyncTask.
I've read that it is not recommend to execute AsyncTask on onCreate().
Where I have to execute AsyncTask on onStart() or onResume()?
I want to left enough time to show activity before execute it.
onCreate(), onStart() and onResume() are lifecycle methods called by the operating system and shouldn't be called directly. You can however override them to have your code executed at these stages of the activities lifecycle:
However, if you want your AsyncTask to start after all of your Views have been inflated and drawn to the screen then you need to put the code in this:
toReturn.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
toReturn.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// asyncTask.execute();
}
});
In the above example toReturn is a view in your onCreate() method. It can be any view you like.
This pulls a ViewTreeObserver from the View and add's a listener to it which will be called when the view has finished being drawn to the screen. It's important you keep the "removeGlobalOnLayoutListener()` line in as this will stop the code firing every time the View is drawn.
Answer is in onResume()
I hade same requirement in my activity where i need to show some list with other buttons and images..
List were getting data from server so used AsyncTask for that..
But before that required to show empty listview and other part of the screen..
so first when it goes to onCreate() I set empty arraylist to listview's adapter then in onResume() call the Asynctask and in that task fill the ArrayList and call adapter.notifyDataSetChanged()
Then another problem occure..when i go to next activity and come back it always call the asynctask even if i dont require..
So had put some condition like if(arrayList.size()==0) then call asynctask else dont.
You can put yur code in the onWindowsFocusChanged method. You can use a thread inside it to manage the timer to start your specific asynctask.
Be aware that this would be performed each time your activity have the focus, not only the first time you launch your activity (I don't know if this could be a problem for you).
implement a View object and override the onDraw().
that way you'll know exactly when the first screen is visible to the user

Fragments being replaced while AsyncTask is executed - NullPointerException on getActivity()

I recently converted my Activities to Fragments.
Using something similar to Tab-Navigation the fragments are replaced when the user selects another tab.
After the fragment is populated I start at least one AsyncTask to get some information from the internet. However - if the user switches to another tab just as the doBackground-method from my AsyncTask is being executed - the fragment is replaced and thus I am getting a NullPointerException in the marked lines:
#Override
protected Object doInBackground(Object... params) {
...
String tempjson = helper.SendPost(getResources().getText(R.string.apiid)); //ERROR: Fragment not attached
...
}
protected onPostExecute(Object result) {
...
getActivity().getContentResolver() //NULLPOINTEREXCEPTION
getView().findViewById(R.id.button) //NULL
...
}
getActivity() and getResources() causes an error because my Fragment is replaced.
Things I've tried:
Calling cancel method on my AsyncTask (won't fix first error nor the second error if the fragment is replaced while onPostExecute() is executed)
checking if getActivity() is null or calling this.isDetached() (not a real fix and I'd need to check it whenever I call getActivity() and so on)
So my question is: what would be the best to get rid of these AsyncTask problems? I did not have these problems using Activities as they weren't "killed" / detached on tab change (which resulted in higher memory usage - the reason why I like to switch to Fragments)
Since AsyncTask is running in the background, your fragment may become detached from its parent activity by the time it finishes. As you've found out, you can use isDetached() to check. There's nothing wrong with that, and you don't have to check every time, just consider the fragment and activity life cycles.
Two other alternatives:
Use Loaders, they are designed to play nicer with fragments
Move your AsyncTask loading to the parent activity and use interfaces to decouple from the fragments. The activity would know whether a fragment is there or not, and act accordingly (by possibly discarding the result if the fragment is gone).
Today I've faced the same problem: when I changed the fragment being displayed if the AsyncTask has not finished yet, and it tries to access the viewto populate it with some more elements, it would return a NullPointerException.
I solved the problem overriding one method of the fragments lifecycle: onDetach(). This method is called in the moment before the fragment is detached from the activity.
What you need to do is to call the cancel() method on your AsyncTask. This will stop the task execution avoid the NullPointerExecption.
Here's a sample of onDetach():
#Override
public void onDetach() {
super.onDetach();
task.cancel(true);
}
Check this page to get more information about fragments lifecycle:
http://developer.android.com/reference/android/app/Fragment.html#Lifecycle
And this to view more about Cancelling a task:
http://developer.android.com/reference/android/os/AsyncTask.html
Have you try calling setRetainInstance(true); in the onCreate() function of your fragment class?

LoaderCallbacks.onLoadFinished not called if orientation change happens during AsyncTaskLoader run

Using android-support-v4.jar and FragmentActivity (no fragments at this point)
I have an AsyncTaskLoader which I start loading and then change the orientation while the background thread is still running. In my logs I see the responses come through to the background requests. The responses complete and I expect onLoadFinished() to be called, but it never is.
As a means of troubleshooting, in the Manifest, if I set android:configChanges="orientation" onLoadFinished() gets called as expected.
My Activity implements the loader callbacks. In the source for LoaderManager.initLoader() I see that if the loader already exists, the new callback is set to the LoaderInfo inner object class but I don't see where Loader.registerListener() is called again. registerListener only seems to be called when LoaderManagerImpl.createAndInstallLoader() is called.
I suspect that since the activity is destroyed and recreated on orientation change and since it is the listener for callbacks, the new activity is not registered to be notified.
Can anyone confirm my understanding and what the solution so that onLoadFinished is called after orientation change?
Nikolay identified the issue - Thank you.
I was calling initLoader fron onResume(). The Android documentation states:
"You typically initialize a Loader within the activity's onCreate()
method, or within the fragment's onActivityCreated() method."
Read "typically" as a bit more emphatic than I did when it comes to dealing with configuration change life cycle.
I moved my initLoader call to onCreate() and that solved my problem.
I think the reason is that in FragmentActivity.onCreate() a collection of LoaderManagers is pulled from LastNonConfigurationInstance and in FragmentActivity.onStart() there is some start up work regarding Loaders and LoaderManagers. Things are already in process by the time onResume() is called. When the Loader needs instantiated for the first time, calling initLoader from outside onCreate() still works.
It's actually not the call to initLoader() in onCreate() that's fixing it. It's the call to getLoaderManager(). In summary, what happens is that when an activity is restarted, it already knows about the loaders. It tries to restart them when your activity hits onStart(), but then it hits this code in FragmentHostCallback.doLoaderStart()*:
void doLoaderStart() {
if (mLoadersStarted) {
return;
}
mLoadersStarted = true;
if (mLoaderManager != null) {
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
// WTF: Why aren't we calling doStart() here?!
}
mCheckedForLoaderManager = true;
}
Since getLoaderManager() wasn't called yet, mLoaderManager is null. It therefore skips the first condition and the call to mLoaderManager.doStart().
You can test this by simply putting a call to getLoaderManager() in onCreate(). You don't need to call init / restart loaders there.
This really seems like a bug to me.
* This is the code path even if you aren't using fragments, so don't get confused by that.

Categories

Resources