I am an experienced Android developer and it is a shame that I am still ineffectively fighting against fundamental issues like persisting UI changes across configuration changes.
I know, this is an all too well known problem and there exists a lot of literature on it. The recommended solution is to setRetainInstance(true) a UI-less fragment:
https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
Best practice: AsyncTask during orientation change
For the sake of completeness, let me draw up the scenario. You have an AsyncTask that performs a Network request and is supposed to update the UI in onPostExecute()
I see some issues with the proposed solution:
The proposed solution says that onPostExecute() is guaranteed to not be called during between the calls to onDetach() and the next onAttach() but what if the AsyncTask returns early(after Activity's onPause() and before the call to onDetach()). If onPostExecute() executes during the period, since onSaveInstanceState() will have been called, any update to the Views will not persist across Activity restart.
Second if the AsyncTask returns such that onPostExecute() executes before the call to Activity's onCreate() then the views will not have been initialized for any view updates to be pushed yet.
Is there any effective solution against this problem that addresses memory leaks and state save across config changes?
Related
Do Activity lifecycle callbacks like onCreate(), onStart(), onResume(), always wait for the former to finish before executing, or is it possible that onStart() gets called before onCreate() finishes, interrupts it, and then onCreate continues after onStart() finishes. Is it even possible that they run concurrently, like can onStart() get called while onCreate() hasn't finished, and then a line of onStart() executes, and then a line of onCreate(), and so on.
I assume this isn't the case, and logging on my app shows it isn't, but I haven't ever seen it mentioned explicitely, because I can't know what it's like on other versions, or devices. I also read that each app gets one thread by default, so I think that means that it can't be concurrent, but I wanna make sure.
The main thing that got me thinking about this is the fact that some people online say that in an Activity onCreateOptionsMenu() is called after onCreate() starts but before it finishes. For an example the first answer here. But another user said somewhere else that on some versions onCreateOptionsMenu() is called inside setContentView(), and I guess that's why that is. So does it go something like this then?
onCreate() -> onCreateOptionsMenu() called, onCreate() paused -> onCreateOptionsMenu() finishes -> onCreate() is resumed
Did I get this right? There is no concurrency involved?
And something like this can't happen for the main callbacks like onCreate(), onStart(), onResume()?
You are referencing very old questions/answers regarding the timing of onCreateOptionsMenu(), and these very old posts refer to ActionBarSherlock which was a library that was not part of the Android framework (and may not have followed the rule that I describe below).
As far as I know, all calls to framework methods on Activity or Fragment (unless specifically documented as otherwise) are made on the main (UI) thread. This means that they cannot be interrupted by any other framework calls, that they run to completion and that they cannot run in parallel. My own personal empirical analysis agrees.
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.
Background
I'm working on making an app better by supporting its landscape mode. One thing that I use a lot is Loaders, or more specifically AsyncTaskLoaders .
Using Loaders allow you to keep doing a background task even if the activity is being re-created due to orientation changes, as opposed to AsyncTask.
The question
I'd like to ask about the lifecycle of Loaders:
When do they get GC-ed ? Do I have to keep track of them, and if one has a bitmap inside, should I abandon it as soon as possible? Do they perhaps get GC-ed only after the activity is really destroyed (and not because of configuration changes) ?
I've noticed they have states of being stopped. Does this somehow allow me to pause them?
If #2 is true, How would I implement a loader that can be paused on some points of itself?
Can fragments also have Loaders? As I've noticed, it's only for activities.
if #4 is false, what is the recommended way to use loaders in the design pattern of navigation-drawer that replaces fragments in the container?
Can AsyncTaskLoader be interrupted like AsyncTask (or threads)? I've looked at its code and at the API, but I can't find it. I've also tried to find a workaround, but I didn't succeed.
If #6 is false, is there an alternative? For example, if I know that the loader doesn't need to load something, I could just stop it right away. One way I can think of is to set a flag (maybe AtomicBoolean, just in case) that will tell it to stop, and check this value sometimes within. Problem is that I will need to add it even inside functions that it uses, while an easier way would be to call "Thread.sleep(0)" or something like that.
Is there somewhere an explanation of the lifecycle of Loaders?
Do AsyncTaskLoaders work together, at the same time, or are they like the default, current behavior of AsyncTask, which runs only on a single thread ?
1.When do they get GC-ed ? Do I have to keep track of them, and if one has a bitmap inside, should I abandon it as soon as possible? Do they perhaps get GC-ed only after the activity is really destroyed (and not because of configuration changes) ?
Since the loader lifecycle is tied to the activity/fragment lifecycle, it is safe to assume that the garbage collection pretty much takes place at the same time. Take a look at #8 for the lifecycle of loaders. Might give you some ideas.
2.I've noticed they have states of being stopped. Does this somehow allow me to pause them?
No, as far as i know loaders do not have a onPause() per say.
3.If #2 is true, How would I implement a loader that can be paused on some points of itself?
I really have no answer to this one. Would like to know a solution to this myself.
4.Can fragments also have Loaders? As I've noticed, it's only for activities.
Of course fragments can have loaders. Just initialize the loaderManager in the onActivityCreated() method
5.if #4 is false, what is the recommended way to use loaders in the design pattern of navigation-drawer that replaces fragments in the container?
4 is true. So this question is irrelevant i guess.
6.Can AsyncTaskLoader be interrupted like AsyncTask (or threads)? I've looked at its code and at the API, but I can't find it. I've also tried to find a workaround, but I didn't succeed.
I am not sure what do you mean interrupting the loaders. But if you mean having something similar to a isCancelled() method, then there is a method called cancelLoad() on the AsyncTaskLoader. The complete flow is like cancelLoad()->cancel()->onCancelled() i think.
7.If #6 is false, is there an alternative? For example, if I know that the loader doesn't need to load something, I could just stop it right away. One way I can think of is to set a flag (maybe AtomicBoolean, just in case) that will tell it to stop, and check this value sometimes within. Problem is that I will need to add it even inside functions that it uses, while an easier way would be to call "Thread.sleep(0)" or something like that.
Irrelevant again?
9.Do AsyncTaskLoaders work together, at the same time, or are they like the default, current behavior of AsyncTask, which runs only on a single thread ?
Runs on a single thread.
8.Is there somewhere an explanation of the lifecycle of Loaders?
To my best of knowledge:
When activity/fragment is created the loader starts -> onStartLoading()
When activity becomes invisible or the fragment is detached the loader stops -> onStopLoading()
No callback when either the activity or the fragment is recreated. The LoaderManager stores the results in a local cache.
When activity/fragment is destroyed -> restartLoader() or destroyLoader() is called and the loader resets.
I hope this helps. I might be a bit off on some of the answers. I am constantly learning new things myself.
Cheers.
I feel like this should have been answered by the documentation in the Activity class, but I'm still not positive -- when is it actually safe to update the UI of an Activity (or a Fragment)? Only when the activity is resumed, or any point between being started and stopped?
For example, the Activity docs state:
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user. Between these two methods you can maintain resources that are needed to show the activity to the user. For example, you can register a BroadcastReceiver in onStart() to monitor for changes that impact your UI, and unregister it in onStop() when the user no longer sees what you are displaying. The onStart() and onStop() methods can be called multiple times, as the activity becomes visible and hidden to the user.
From that reading, I would assume that even if a foreground dialog is displaying, I can safely update UI elements behind it.
EDIT
To clarify: the reason I ask is having been bitten by errors when firing off an AsyncTask, and attempting to update the UI in the onPostExecute method. Even though it runs on the UI thread, the user has navigated away from that view and I would receive an exception. I'm starting a new project now and trying to establish some guidelines around better AsyncTask idioms.
I guess this comes down to what you mean by "safe" to update the UI. For elements on the Activity screen, they can be updated whenever, even if your Activity is not in the foreground (but make sure to update from the UI Thread).
However, the issue that will trip you up is saving state: onSaveInstanceState.
As you may know, the Activity that is in the background may be destroyed by the OS to free up memory. It will then be re-created when you come back to it. During this process, the onSaveInstanceState method will be called. If the OS does destroy the Activity, any changes you made to the UI State after the call to onSaveInstanceState will not be persisted.
For Fragments, you will actually get an IllegalStateException if you try to commit a FragmentTransaction after onSaveInstanceState. More info on that.
To summarize, you can update the UI of your activity at any point and try to gracefully handle the Fragment issues, but you may lose these updates when the Activity is restored.
So, you could say that it is only truly safe to update the Activity while it is in the foreground, or rather, before the call to onSaveInstanceState.
Edit in regards to Async Task onPostExectue
This is likely related to the issue I am referring to above with Fragment State Loss. From the blog post I linked to:
Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:
An activity executes an AsyncTask.
The user presses the "Home" key, causing the activity's onSaveInstanceState() and onStop() methods to be called.
The AsyncTask completes and onPostExecute() is called, unaware that the Activity has since been stopped.
A FragmentTransaction is committed inside the onPostExecute() method, causing an exception to be thrown.