I am using this code to clear up my backstack up to my main view:
while(mFragmentManager.getBackStackEntryCount() > 1) {
mFragmentManager.popBackStack(null, 0);
}
I am pretty sure this code was working before, but now the backstack count does not change and no Fragment is being removed, which causes an out of memory exception because the while loop keeps running.
Does anyone know, if there is something wrong with it, or if there is a bug in the latest revision of the SDK-tools. I have no clue what's causing the problem.
You are probably looking for popBackStackImmediate(). This immediately executes a back stack pop.
you can also use .executePendingTransactions(); after popBackStack();
I certainly would not assume that popBackStack() has an immediate effect, which your loop appears to do.
What documentation says for
popBackStack():
Pop all back stack states up to the one with the given identifier.
This function is asynchronous -- it enqueues the request to pop, but
the action will not be performed until the application returns to its
event loop.
So after mFragmentManager.popBackStack(null, 0); your backstack is not empty until your event is completely processed.
Use popBackStackImmediate() for empty it immediately in the current event itself.
Related
I want to know if anything got popped of when I called getFragmentManager().popBackStack(). How can I find this out?
I'm using android.support.v4.app.FragmentManager.
What I tried
Way 1: return value
In the documentation for android.support.v4.app.FragmentManager.popBackStack(), it says:
Pop the top state off the back stack. Returns true if there was one to pop, else false.
So according to that, it should work like this:
boolean popped = getFragmentManager().popBackStack();
But this doesn't work because the return value of popBackStack() is void.
Is this either a bug in their code or in their documentation?
Way 2: back stack count
I tried to calculate from getFragmentManager().getBackStackEntryCount() if anything was popped:
int countBefore = getFragmentManager().getBackStackEntryCount();
getFragmentManager().popBackStack();
int countAfter = getFragmentManager().getBackStackEntryCount();
boolean popped = countAfter != countBefore;
But this also doesn't work because of how popBackStack() works:
This function is asynchronous -- it enqueues the request to pop, but
the action will not be performed until the application returns to its
event loop.
So this means that both calls to getBackStackEntryCount() give me the same number.
Questions
How can I find out if anything was popped?
What I described in way 1: Is either a bug in Android's code or in Android's documentation?
If it's a bug: How can I tell them, that there's something mixed up? (I'm asking this because once I already tried to fix a mistake in their Android documentation but until today they never merged my pull request)
Question: How can I find out if anything was popped?
There are two methods for popping back stack:
void popBackStack()
Pop the top state off the back stack. This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.
boolean popBackStackImmediate()
Like popBackStack(), but performs the operation immediately inside of the call. This is like calling executePendingTransactions() afterwards without forcing the start of postponed Transactions.
The latter one seems like the one you were looking for.
Alternatively you could addOnBackStackChangedListener in onStart and unregister it in onStop if you need to observe changes at all times.
Question: What I described in way 1: Is either a bug in Android's code or in Android's documentation?
Quick glance in the docs reveals both methods with appropriate description. Support library looks intact as well. This was probably an oversight on your part.
EDIT: Ok, I glanced too quickly. It does say "Pop the top state off the back stack. Returns true if there was one to pop, else false. This function is asynchronous [...]". This is of course wrong.
Question: If it's a bug: How can I tell them, that there's something mixed up?
GitHub repo is only a mirror. The Android dev team does not go there and merge pull requests. If you wish to contribute, use AOSP gerrit. More info here: https://source.android.com/source/contributing
Alternatively you can file a bug report here: https://issuetracker.google.com/issues
Could some one please tell me the difference between popBackStackImmediate and popBackStack ? I really don't get it and additionally what is the "flag" 0(zero) means in 2nd of popBackStack?
Thank you very much for your helps guys...
popBackStackImmediate() will perform the popping commands immediately in the call. The results of which can be verified immediately after the call. It is somewhat slower since all the popping actions are performed within the call.
popBackStack() will perform the popping commands within the next event loop cycle (i.e. next draw phase). So it's asynchronous to the rest of the code. That means the FragmentTransaction will not be removed from the backstack after this is executed. In most cases you don't need the FragmentTransaction immediately popped, so it waits until everything else is finished before it actually happens. All this happens so fast that the user wouldn't recognize it.
The flag at the end is unrelated. It can currently only be set to POP_BACK_STACK_INCLUSIVE. The FragmentManager allows you to set an ID on the backstack. If you set the flag, then it will pop the FragmentTransaction that match the ID specified until there is one that does not match the ID or the bottom is reached. If the flag is not set, then all FragmentTransactions that don't match the ID are popped until one is reached that does match the ID or the bottom is reached.
popBackStack() will pop the back stack, but it won't perform the pop until slightly later - it posts a message to do it so you don't have to wait for a heavyweight operation to occur.
popBackStackImmediate() does it right now, before the function returns. Its slower and can cause perf issues. Use the non-immediate version when possible.
0 as the second parameter means to use the default behavior (remove the top element in the backstack). You can also pass it a series of boolean ORed flags. The only flag currently supported is POP_BACK_STACK_INCLUSIVE, which changes it to take out multiple fragments.
I ran into a very strange issue with postponing transition.
I postponed the return transition in onActivityReenter. But the problem here is that if I checked „Don’t keep activities“ in the settings of the phone, the app will most of the times just hang and show both activities over each other.
I found out that in that case onActivityReenter still gets called. But onPreDraw doesn’t, which should be called by the onPreDrawListener and resumes the postponed transition. So I postpone the transition forever.
What could have happened there?
The code can be found here.
I've been running into the same issue. It's only in specific cases, and even then not 100% of the time.
Requesting the layout again after adding the listener works for me.
myView.getViewTreeObserver().addOnPreDrawListener(<snip>);
myView.requestLayout(); //Force a layout pass
I'm pretty sure this is indicative of a bug somewhere in Android, but I haven't been able to create a reliable test case. Somehow, it's skipping part of the layout pass, so the listener doesn't ever get called. Calling requestLayout() forces the full layout pass to happen.
I am sending an HTTP request to some server(in a background thread) inside onCreateView() method within my fragment class.
When the request received i check some stuff and according to that i add a new fragment(through fragment transaction in the UI thread).
I use the support library
But, for example, if the user press the android home button, while the request hasn't received yet, the fragment is going to pause or stop state and then the request received and of course the exception is thrown(just after trying to commit).
I searched the web and found some very good articles and answers which are relevant to this issue and all the things related to the 'state loss' after onSaveInstanceState() has been called, etc...
For example i read this excellent article of Alex Lockwood: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
and also those stackoverflow questions/answers: getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"
and: "Can not perform this action after onSaveInstanceState" - why am I getting this exception from my activity's onResume method? and more.
But i couldn't came to a conclusion of what to do in my case.
The only thing that came to mind is to use commitAllowingStateLoss() instead of commit() but this fills hacky and not correct as some of the answers that i read conclude about it.
Also there was a suggestion to commit the transaction in onCreate() because it safe, but of course it can't work in my situation, all other suggestions doesn't work for me as well.
Maybe i can use some boolean flag to check if i can't make a transaction and then wait for the fragment to resume and if the flag is true then do the transaction.
This fills to much work and also presents the problem in which i need to know if the transaction can be done(don't even know how to check it).
I have a case where I have two clickable items in a fragment. Item A detaches the current fragment from the current activity, and attaches a new fragment. Item B causes some UI updates. My problem is, if I click on both of them in quick succession, the app crashes. I get a NullPointerException in the UI updates because apparently the fragment has already been detached from item A. A more visual representation of the log:
Click item B
Click item A shortly thereafter
Item A causes the current fragment to detach, and eventually causes onDestroyView to be called.
Item B attempts to update the UI about 150ms later, but the UI elements are now null because onDestroyView has already executed, causing a NPE.
I'm using a Nexus 4 running 4.2.2. I'm using Dagger to do dependency injection, which I thought could be the source of the issues, but I'm seeing similar edge cases throughout my app where two things can be clicked on almost simultaneously and the crashes sometimes happen with things that aren't injected. In each case, both actions will happen in a seemingly undefined order, which causes weird behavior and crashes.
Is there a way to stop all subsequent or in flight events in onStop or onDestroyView? Or do I need to add an isDetached() check to every one of my UI listeners? I also tried detaching all of my listeners in onDestroy, which eliminated some, but not all of the crashes. It seems that I'm having a similar issue to this unanswered question: Fragment's OnClickListener called after onDestroyView.
When you finish the Activity explicitly, it takes sometimes to destroy the fragment, so the following check will be more generic.
if(getActivity() == null || getActivity().isFinishing() ||
fragment.isRemoving() || fragment.isDetached() ||
!fragment.isAdded()) {
// fragment is stopped
}
It is not possible to give exact solution to the problem without seeing your actual implementation, but here is what I'd do:
Pass the instance of the fragment to the method where I am doing the UI update and check if the fragment is currently being removed, is added or was not already detached from the UI (or any combination of these) before actually attempting the update. Example:
private static void updateUi(MyFragment fragment, Object param) {
if (fragment.isRemoving() || fragment.isDetached() || !fragment.isAdded()) {
return;
}
// Update the UI
}
Hope this helps.