In my app I have several AsyncTasks doing http requests.
If a task fails I display a DialogFragment to the user.
ErrorDialogFragment newFragment = ErrorDialogFragment.newInstance(R.string.dialog_message_error);
newFragment.show(getFragmentManager(), ConstantHandler.DIALOG_ERROR);
The problem is that the user sometimes quits the Activity or the Application before the AsyncTask is completed, causing a null pointer exception on the getFragmentManager.
My question is if there is a way to avoid this except for keeping track on if each fragment and activity is still running?
I've had the same issues before. A reasonably good answer I saw on a blog somewhere (I've forgotten) is as follows:
Give your AsyncTasks a reference to the current context. When your app is minimised/rotated, save your AsyncTasks using onRetainNonConfigurationInstance() and getLastNonConfigurationInstance(). Then when your app is restarted, in onCreate(), you can check if there is an AsyncTask passed back from getLastNonConfigurationInstance(). If there is, set it's context to the new context of your activity. Now your AsyncTask will be able to access the new Context and complete its task.
To be thorough, you probably also want to set your context to null in your AsyncTasks before you save them using onRetainNonConfigurationInstance(). Then in your AsyncTask, check if the Context is null before trying to do something using it. This will stop the app crashing if the AsyncTask tries to use the context while in the background.
Related
I have a pretty odd problem here. In a fragment, I do a process and when the process finishes I show an advert that callsback to the fragment when user clicks/dismisses the ad. The problem is that in some devices when the ad calls back to the handler (that is in the running fragment) the activity containing the fragment has been destroyed, but I need to do some more work through a runnable. So, in this case the runnable throws a NullPointerException int is run method when executed.
I could just check if the activity is still alive and run just the runnable when it is, but in the cases it is not alive I still need to continue to do the part of the job that needs to be done after the ad.
How do you handle this kind of situations? I have been thinking about the problem during some hours without finding a solution to this.
Thanks in advance.
You can use AsyncTask in this case .
AsyncTask processes are not automatically killed by the OS. AsyncTask processes run in the background and is responsible for finishing it's own job in any case. You can cancel your AsycnTask by calling cancel(true) method. This will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object) method is called instead of onPostExecute() after doInBackground() returns.
Hope it helps..
mmm the way this is asked I am not sure what you are asking, perhaps some text connectors might work, I am not sure if this is a quite basic question about state changes or a very complex one.
from what I understood:
wouldn't this be the same problem as when you flip screen? make a Bundle of the data that is restored through activity changes. This way if your activity has been Destroyed you restore it
fragments have a feature that you can use to keep instance alive across a configuration change: retainInstance
setRetainInstance(true) // false by default
Parcelable like Serializable, is an API for saving an object out to a stream of bytes. Objects may elect to implement the Parcelable interface if they are what we will call "stashable" here. Objects are stashed in Java by putting them in a Bundle, or by marking them Serializable so they can be serialized, or by implementing the Parcelable interface. Whichever way you do it, the same idea applies: you should not be using any of these tools unless your object is stashable
---or---
turn that "advert" of yours into an Alert, which wont mess with the Activity.
---or---
run the response on a different thread?
I know you are using it quite well with your Non-UI codes in AsnycTask but I am just wondering if there is any kind of problem while using AsyntTask? I am not having any code which produce the problem. But I am just curious to know Any bad experience if you have with AsnycTask and would like to share it.
Memory Leak :
Even though activity is destroyed, AsyncTask holds the Activity's reference since it has to update UI with the callback methods.
cancelling AsyncTask :
cancelling AsyncTask using cancel() API will not make sure that task will stop immediately.
Data lose :
When screen orientation is done. Activity is destroyed and recreated, hence AsysncTask will hold invalid reference of activity and will trouble in updating UI.
Concurrent AsyncTasks: Open Asynctask.java go to line number 199, it shows you can create only 128 concurrent tasks
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
Rotation: When Activity is restarted, your AsyncTask’s reference to the Activity is no longer valid, so onPostExecute() will have no effect.
Cancelling AsyncTasks: If you AsyncTask.cancel() it does not cancel your AsyncTask. It’s up to you to check whether the AsyncTask has been canceled or not.
Lifecycle: AsyncTask is not linked with Activity or Fragment, so you have to manage the cancellation of AsyncTask.
There are some workarounds to solve above issues for more details have a look at The Hidden Pitfalls of AsyncTask
I just want to share the information that if you are using Asynctask, it will keep on doing its work even of the activity does not exist.
So in case you have asynctask which starts in onCreate() of the activity, and you rotate the device. At each rotation, a new activity is created with a new instance of Asysntask. So many requests will be send over the network for same task.In this way, a lot of memory will be consumed which effects the app performance resulting in crashing it. So to deal with it Loaders(Asynctask Loaders) are used.
For more info check the video:
Loaders
I have read that an AsyncTask keeps running even if the calling activity has stopped. Keeping this in mind, I am trying to understand the working of onTaskCompleted. onTaskCompleted is usually called from onPostExecute() of the AsyncTask and if the calling activity still exists, everything is good. But what if the calling activity has stopped? Will this result in a crash of the app?
This question came to me because I was thinking of updating some global variables or UI components in the calling activity on the result of the AsyncTask. Is this a good idea? Won't this also result in a crash?
Thanks.
I have previously had my App working with just activities and am now working on converting to fragments in order to improve the UI.
Previously my Activity started an AsyncTask and passed in itself to be used as the Context when certain methods required it (not UI operations, but calls to shared preferences and content providers). I have now learnt that this approach can lead to undesirable outcomes if the Activity is destroyed and garbage collected, but it did compile and run fine.
I began this change because I wanted to make my loading screen behave better when the app was paused and stopped. I realised people frown on loading screens in Android but for me it is required as I have an operation that will take 20 seconds or so and that needs to be completed before the app will function.
So using this guide, I began improving my app.
In short the guide moves the AsyncTask into a Fragment that does not have an attached UI, with a separate Fragment for displaying the loading screen with ProgressBar. This means that the Fragment that spawns the AsyncTask does not have a Context, meaning I cant pass one in to the AsyncTask.
As I said before I have operations in the AsyncTask that require a Context object, so where can I get it from? Should I just pass in that data to the AsyncTask before I start?
As far as I know, the context is not a static property, so you actually need one object to retrieve it.
Thus, you can either go the "hack-way" as in this post:
Static way to get 'Context' on Android?
or you can follow Android guidelines and use a Service for your background loading. Remember that AsyncTask is an utility class designed to help in background operations that later need to communicate with the UI, so you should use AsyncTask in correlation with a UI object.
If you, instead use a Service, then you got no problem, since the Service object itself is the context that you need.
If your AsyncTask is not handling any UI components you can use the parent Activity's context. So where you previously passed in this you'll now pass in getActivity(). Note, if you do have it changing the ui this will set you up for Null Pointer Exceptions.
If I have a DialogFragment that is retained and uncancelable, in what situations can getActivity() return null?
As far as I understand, the only ways an Activity can be destroyed are:
Via the back button - Which can't happen since my dialog is uncancelable
Via a rotate - This (I think) won't matter since my dialog is retained and the Activity will be re-attached (due to activity recreation in one main thread message) before any of my main thread callbacks are run.
The question I have is, am I missing a case? Or are my assumptions incorrect?
Not exactly an answer to your question, but I had a getActivity() to return null when used a Handler in a DialogFragment. Even if Handler had a link to DialogFragment instance it still had getActivity() as null in handleMessage() method. Would be glad to hear why does it happen.
P.S. Even more strange, what the first time this DialogFragment is created everything is fine. When we create a new instance of this DialogFragment he getActivity() in Handler starts to return null.
I suspect, that while we create a new DialogFragment, the Handler inside somehow still attached to the old one (which already is detached and doesn't have activity)
Must admit, that my Handler is not static as recommended.
P.P.S I use compat lib for fragments
Ok. After giving it some thought I think I understood my error. The handler from the first DialogFragment was registered in my service and was never unregistered. So when the message comes it goes into this handler as well. I believe it can cause a memory leak if not already :D