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.
Related
I understand the basic Lifecycle of Activity/Fragment but sometimes, when user puts the app in background state for long time I'm not sure what's going to happen when he opens it again. Recently I've encountered a bug: User gets nullpointer exception by calling method of a view saved in class variable (textView = findViewById(...)), inside fragment's OnResume method. The variable is set in OnViewCreated(). Is that possible that over long period of time fragment might lose it's fields due to lack of memory? When onResume() will be called and when onCreate()?
If the app is in background for a long time its process will be killed by OS, or if the device is running low memory. To test how your app works under these conditions use flag "Do not keep activities" in Developer options on your device. In the described case onCreate will be called when Activity will come to the foreground.
If the process is not yet killed then onResume will be triggered. Normal variables persist, but the problem is that you can never be sure when you're calling onResume and when you're calling onCreate (since you have no control over when Android just goes and tosses stuff on the stack out the window... anything not currently being used is eligible for destruction).
So my solution is to add a null check with if condition: if the variable is null then initialize and perform actions, if not then just preform actions.
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 a stock Nexus 5 running 4.4.2 (using ART if it matters) and I've found an interesting scenario. I have this as my onDestroy():
#Override
protected void onDestroy() {
super.onDestroy();
t.setText("onDestroy");
t.show();
}
It's a GPS oriented app so I'm up and walking around. I am using the technique mentioned in this question to show a lot of debug toast messages.
Anyway, when I rotate my app, the toast appears. I understand that the activity is destroyed and recreated for the new orientation, but how can I know what's really going on? How can I tell when my app is REALLY getting destroyed and not just being rotated? Similar to this question, I want to log out when a particular activity is destroyed.
Since Honeycomb, the isChangingConfigurations() method can be queried to check whether the Activity is being recreated due to configuration changes. Alternatively, the isFinishing() method can be queried on any API level to check whether the Activity is actually being finished, or is only being destroyed temporarily by the system.
As far as I can determine, the two methods should always return mutually consistent results in practice. The only point where they might have diverged is when the system kills the process to clear memory, but there are no callbacks or interaction with the app at that point.
The documentation of the onDestroy() method mentions the use of the isFinishing() method:
Perform any final cleanup before an activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
You can put it in a fragment with setRetainInstanceState(true) set. Place your code in the onDestroy() method of the fragment. Then, the fragment will not be destroyed on orientation changes.
First of all, you should not use onDestroy() to do anything because its not guaranteed to be called. I would put things on the onPause() method; I wouldn't even put things in onStop().
Also, Im not sure why you want to log out a user when they navigate away from the app. I would rather implement some kind of timer on the app or server to log out after x time.
Now, the answer lies in the documentation: http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
You might want to override onConfigurationChanged so that your activity is not restarted.
I found a couple of solutions which are really just patterns to detect when the screen rotates. Alternatively, you can determine that the device was actually destroyed by checking some static data member to see if it was initialized or not.
Configuration changed solutions:
The first one involves handling all of the configuration changes in the onConfigurationChanged callback.
"Note that this will only be called if you have selected
configurations you would like to handle with the configChanges
attribute in your manifest."
The second involves listening for Display.getRotation() which returns a Surface.ROTATION_* object. Which is the new orientation of your screen relative to the natural state of the device orientation.
Again, you can use the configuration changes along with the static member.
Add ConfigChanges.UiMode flag to the ConfigurationChanges attribute for your MainActivity class, and this solves the problem.
More details: Android Launcher "OnDestroy" gets called twice
Whats the recommaned approach to notifying the hosting activity of a fragment that performs some background processing, that its done. Assuming that the fragments are running some threads that are performing work outside of the main looper.
A simple callback won't do since:
The Activity could be detached due to screen rotation which would lead into a NullPointerException.
Polling from within the activity is just dumb
Only calling the activity once if attached and let the activity check after each onCreate call (i.e. due to screen rotation).
What I currently do but it seems wrong: Whenever the Fragment gets attached, it will check if the work is done and notify the activity via callback. If the fragment finishes the work it will also callback the activity (if attached).
Why I think is is wrong? Because I have some really ugly methods that check if the fragment is attached and also if the work is done in order to maybe call the callback. This becomes very stupid when an exception is raised during performing some work in the fragment and the activity is detached. If android decides to call onSaveInstance in the same moment I will have to put the Exception into the Bundle and deliver it later when the Activity and fragment is recreated from the saved state. Additionally I can run into a situation where a activity will received the same callback twice (once from checking the fragment and the second time when the fragments gets attached; this could happen when the application got saved and restored)
This generates so much code that, in my optinion, could be much more clear if activites won't get detached. That is why I hope I'm doing something wrong and hope someone will provide me with a better solution.
I would say you should use a service for your background processing but if you've chosen a fragment for a specific reason you can stick with that. The way you should notify the activity from either a fragment or a service that might have a different lifecycle from the activity would be through a BroadcastReceiver. With this method the activity can register and unregister a BroadcastReceiver during its own lifecycle callback. From the fragment or service you just send the broadcast and forget about it. If an activity is listening for the broadcast it will receive it, if not nothing will happen.
I have manager classes that take an activity as a listener. I use the managers to do threaded calls, work etc and then call back to the listener(activity) when things are done, need changed and so on.
I want to register and unregister the activity as a listener when it is no longer visible. This will prevent unwanted changes from happening (like dialogs appearing when the activity is no longer visible).
My question is, what lifecycle events are best to do this registering. I started with onPause() and onResume() which worked well except when I had an activity that was doing stuff in onActivityResult(). Since onActivityResult() gets called before onResume() my managers are not always registered in time.
Do I need to register in onResume() AND onActivityResult() or is there a better way to approach this?
An alternative approach may be to postpone the processing currently done in onActivityResult() until after the listeners are registered in onResume().
Possible ways of doing this include posting to the message queue, e.g. using a Handler, setting a Runnable object to be called by onResume, or simply storing the result data received by onActivityResult().
This would also ensure that the activity really has come to the foreground when the listener methods are called.
onResume() and onPause() are the best for this. The onDestroy(), per the documentation, is not guaranteed to be invoked though this is a favorite for many people, so stick with the pauses and resumes.
You can have the handle of the current Activity in the Manager class. Register its presence on onCreate() and unregister it on either onCreate() by some other Activity, or onBackPressed() of the current Activity.
On a related note, I would recommend an MVC (or similar) architecture where the controller has awareness of the view's status (the controller can track the onCreate() and onBackPressed() of each Activity).