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
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 have several Fragments that are hosted by an activity. They register listeners that are called from a custom Application class in onResume() and unregister them in onPause().
The Activity sometimes exchanges Fragments by using fragmentTransition.replace(...)
Sometimes (quite rare) getActivity() that is called in the listeners returns null.
How is that possible? The listener should not be called because he should unregister first?
To force this, you can install my app and click on a cover (calls replace on a new fragment) and turn the device (runtime change) at the same time.
Thanks for the contribution. I fixed this issue. It was caused by a very rare race condition. I locked the accessors correctly, but assumed something wrong, when I used a handler to post to the ui thread. This had a very tiny delay that caused the race condition to fail.
I fixed it by using getActivity().runOnUiThread(...) instead which has no delay.
Sorry for bothering with this very specific problem.
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.
Is it a good practice to use getApplicationContext() working with AsyncTask in order to do not have to attach and detach the Activity to avoid memory leaks when rotation changes occur and the Activity is destroyed? I thing it should be correct, as I actually need a Context that depends on the hole application, not the Activity itself.
And what is more, in those cases in which is better to use the Activity as context (because you need access to the Activity showing)... Instead of detaching it (assigning to null) when it is destroyed and then assign the new instance in onCreate(), could be just avoid detaching? So, just reasigning the new instance, this way, we could avoid problems of NullPointerException because there would always be a context to use!
This post and its answers are a good explanation of what to do. This post and its answers explain some good theory behind the AsyncTask and Context issue.
From my own experience I can say that in most cases it is better to use Activity as your Context, than getApplicationContext() when dealing with AsyncTask. This is because most of times you will need to access members from your Activity and you will only be allowed to do so if you have a reference to the Activity in your AsyncTask.
To answer my question about avoiding detach(), let me say that in this case you can avoid it or just do it without any problems, as #CommonsWare states in his answer. So, from what he says we are sure that we will not get a NullPointerException while the Activity is null during a rotation change:
onProgressUpdate() and onPostExecute() are suspended between the start of onRetainNonConfigurationInstance() and the end of the subsequent onCreate()
If I am not wrong, the main difference between not detaching explicitly and just re-attaching the new one in the onCreate() of the newly created Activity is that you free the old Activity instance some milliseconds later when just reattaching. But the final behavior is the same in both cases!
Hope this helps someone else! :)
When it comes to threads and orientation changes, it seems the normal thing to do is something like this:
public class Bwent extends Activity {
private static Bwent instance;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
instance = this;
}
//...
That way, if you're making a network request with a thread, and someone changes the orientation of the phone, the thread will know to use the new Activity.
However, is it possible that the thread could finish during the time Android is destroying the old Activity and creating a new one?
Is there a moment in the process where the thread still might be pointing to the wrong Activity, or a partially destroyed activity?
It seems like there shouldn't be, but even using a Handler created in the main thread, I'm having intermittent issues with a thread trying to update an object that no longer exists. It's rare, but it does happen.
When it comes to threads and
orientation changes, it seems the
normal thing to do is something like
this:
It is a thing to do. I am not certain whether or not it is the "normal" thing to do. I am dubious that it is the best thing to do.
However, is it possible that the
thread could finish during the time
Android is destroying the old Activity
and creating a new one?
Yes. There is nothing in your code preventing it.
Is there a moment in the process where
the thread still might be pointing to
the wrong Activity, or a partially
destroyed activity?
Yes. There is nothing in your code preventing it.
Instead, try the pattern that I illustrate here. Use an AsyncTask, implemented as a static inner class or a public class. Have it be the one that knows about the Activity. Have it only use the Activity in doPostExecute() (or possibly onPublishProgress()). From the way AsyncTask and Handler work, our understanding is that the AsyncTask will always have an Activity in those on-the-main-thread methods.
Some of this stuff was discussed recently.