difference between popBackStackImmediate vs popBackStack - android

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.

Related

Correct way to close DialogFragment when using Navigation component?

I am using the Navigation component to show a DialogFragment (<dialog...>...</dialog> in the navigation.xml) and want to know what's the recommended way to close the Dialog. I tried myself and got the following results:
1) dismiss()in DialogFragment: seems to work fine
2) findNavController().navigateUp(): seems to work fine
3) findNavController().navigate(MyDialogFragmentDirections.actionMyDialogFragmentToMyNormalFragment()): works, but loads a fresh version of the target destination, so depending on the use case this might not be what one wants to have.
Note: My use case is that MyNormalFragmentuses MyDialogFragmentto get some input, so after MyDialogFragmentis shown, I need to get back to the already existing instance of MyNormalFragment.
So for me, only 1) or 2) is correct. Now I am wondering, is there any difference between 1) and 2) ?
Both 1) and 2) end up doing the same thing the end, but 2) is always a safer option.
When you call dismiss(), the DialogFragment is dismissed and the DialogFragment is stopped (it receives a callback to onStop()). This triggers the listener in DialogFragmentNavigator, which then updates the NavController's state by calling popBackStack().
dismiss() however, is an asynchronous operation (as seen in the DialogFragment source code - you'll note it does not use commitNow(), etc). Therefore if you were to check what destination you are on from the NavController.getCurrentDestination(), you'd see that you're still on the dialog destination, despite having triggered the dismissal.
navigateUp(), on the other hand, goes directly to the NavController. Since you have another destination on your back stack (the one under the DialogFragment), the NavController source code shows that navigateUp() just calls popBackStack() - the same operation that dismiss() was eventually triggering.
However, when it is the NavController that is driving the operation, NavController updates its state synchronously. This means that immediately after you call navigateUp(), it will have updated its getCurrentDestination() and internal state in addition to calling through to DialogFragmentNavigator's popBackStack(), which is what calls through to dismiss() (removing the observer mentioned above to prevent double dismissals).
Therefore calling navigateUp() is always the safer choice since it ensures that the NavController is synchronously updated to the correct state, rather than rely on FragmentManager's asynchronous timing (which may mean that additional click events are received during that time period due to multi-touch, etc.).
Calling navigate() with an action that has an app:destination on it will navigate to a new instance of the destination, which would not be appropriate for returning back to your previous instance.

How to know if FragmentManager's back stack popped anything?

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

Android Activity from Notification - FragmentManager backstack is zero

I searched around but couldn't find an answer.
My app is based on fragments, and MainActivity is the entry point. When the user clicks on a notification, I trigger the desired behaviour with a PendingIntent that contains specific flags as to what fragment needs to be invoked.
My problem occurs when the fragment I need is already being displayed. When the MainActivity is entered through the PendingIntent, the fragmentManager.getBackStackEntryCount() will return zero. I guess that this happens because the activity is a different instance than what I already had running. Then the same fragment is invoked again and this messes up my UI (and potentially crash because the Otto bus had already been registered).
My question is: what is the best way to detect the state of the running app (i.e. which fragment is being displayed) when the activity is triggered from a notification? Thanks.
I guess you could do that with the FragmentManager class.
You have several interesting methods. For example, findFragmentByTag(String tag) enables you to know if a Fragment with a particular TAG exists.
Also, are you sure you are not recreating a new Activity (on top of the current one ) ?
Thanks to #Gordak suggestion, I was able to figure out my problem. What I needed was a combination of flags in my (pending)Intent:
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP);
This way, if the invoked activity is on top, it won't be destroyed (thus keeping the FragmentManager's backstack) but the method onNewIntent(Intent intent) will be triggered, letting you react to it. This is described in the documentation for
FLAG_ACTIVITY_CLEAR_TOP.

FragmentManager popBackStack does not work - Bug?

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.

Same Activity called twice... Issue with Multiple AsyncTasks?

I have three simultaneous instances of an AsyncTask for download three files. When two particular ones finish, at the end of onPostExecute() I check a flag set by each, and if both are true, I call startActivity() for the next Activity.
I am currently seeing the activity called twice, or something that resembles this type of behavior. Since the screen does that 'swipe left' kind of transition to the next activity, it sometimes does it twice (and when I hit back, it goes back to the same activity). It's obvious two versions of the activity that SHOULD only get called once are being put on the Activity stack.
Could this be from both onPostExecute()s executing simultaneously and both checking the flags each other set at the exact same time? This seems extremely unlikely since two processes would have to be running line-by-line in parallel...
*****EDIT*** A lot removed from this question since I was way off in what I thought was wrong. Nonetheless I found the answer here quite useful, so I have edited the question to reflect the useful parts.
The only way I can find that this is
possible is if both AsyncTasks'
onPostExecute() executed SO
simultaneously that they were
virtually running the same lines at
the same time, since I set the
'itemXdownloaded' flag to true right
before I check for both and call
startActivity().
Since they are both called on the main application thread, that's not possible, unless you're doing something really strange.
I would introduce some Log calls to ensure that you are not misreading the symptoms.
Beyond that, it is difficult to see any problems from your pseudocode, unless there's a possibility of other downloadID values beyond the three shown. For example, if there is a DL4, and DL4 completed after DL1 and DL2, DL4 would trigger your activity.

Categories

Resources