How to avoid multiple instances of a fragment in back stack? - android

I want to avoid that in this navigation use case: A -> B -> A -> B -> A -> B ...
Where all the fragment instances are kept in the back stack. Reason: Avoid out of memory error.
I tried creating an own navigation workflow, like described here: https://stackoverflow.com/questions/18041583/fragments-backstack-issue?noredirect=1#comment26393904_18041583 (which is supposed to mimic activity behaviour calling always finish() after starting a new one, together with letting only the very first one (home) in the navigation stack).
But it seems to be either very wrong or ununderstandable.
So I thought, also, to implement a behaviour like activity "bring to front" flag. But I don't know how to do it. Maybe something with popBackStack - but I don't know how to ask the fragment if the transaction already is in the backstack. And I don't know if I'm on the right path.
This should be a quite standard task, since every navigation menu basically has this problem. But still, seems not to be straight forward to implement, and also can't find information about it.
Any idea?

Take a look at the FragmentManager backstack. It has facilities for looking at/popping entries in the fragment backstack. You might want logic something along the lines of: if the user is asking for a fragment that is at the top of the stack (the previous fragment) then exit this fragment (go back) otherwise start a new one.
That would produce:
A (user asks for B)
A->B (user asks for A again)
A
.. but would not prevent
A (user asks for B)
A->B (user asks for C)
A->B->C (user asks for A)
A->B->C->A
That would require rewinding the stack back to "A" from "C", which you can do.. but then if that is the case, perhaps you should be unconditionally popping the fragment stack before starting a new fragment (I.E. No back stack at all..)

Related

Make FragmentTransaction From Activity Started From Fragment

So right now I have a situation in which I have three fragments are committed in such an order:
Fragment A -> Fragment B -> Fragment C
Then, I start an Activity from Fragment C. The issue arises when I want to pop the back stack so the user is brought to Fragment B after the activity finishes. If I attempt to pop the back stack from the Activity before calling finish(), I get an IllegalStateException, saying that the action cannot be performed after onSaveInstanceState. Thus, is it even possible to make changes to the FragmentManager responsible for the fragments from the Activity?
How does this sound myrocks2? Android: how to make an activity return results to the activity which calls it?
First activity can start a second activity and expect a result. Upon getting back a result it knows second activity did its job, and now it's required to remove fragment c. (I don't know the logic of your app, but that can work)
Someone who thinks he is so smart gave you a negative vote, but I made sure to go away. There are no dumb questions.

Restoring fragment backstack for fragment with two possible backstack paths

I have one Activity with 3 fragments which form a workflow to collect user input.
Normally, Fragment A is the first fragment -> Launches B -> Launches C. B is supposed to launch A if the back button is pressed, and similarly C's Back button is supposed to launch B.
However, in some cases, A is supposed to launch C directly, and then C's back should launch A again.
I prefer that C should not know who launched it. I.e. I want C's "backstack" to operate without C knowing who launched it.
I tried using the usual addToBackstack approach, but I'm facing a problem when the Activity gets killed after the user lets the app go into the background while C was open.
I would like the user to return to "C" instead of starting all over from A. To achieve this I'm using the saved Instance state, and detecting which fragment was previously active, and launching it from the activity.
The problem starts when the user wants to go back from C, after the Activity was recreated after being killed. C doesn't know who launched it: A or B. How do I make the Back button work as expected for this case?
Found some answers in this excellent video by Android Developers + Adam Powell:
Fragments: Google I/O 2016
In summary, Fragments and the Fragment BackStack are considered part of the navigational state of the app, so, as long as I don't mess with the backstack when the activity is launched, the OS will also restore the FragmentBackStack, thus the BackStack will know who launched C (A or B) even if the activity gets re-created. Thus, popBackStack will move from C to A or B as required.

What are the implications of using FLAG_ACTIVITY_SINGLE_TOP?

Could someone please explain what FLAG_ACTIVITY_SINGLE_TOP do? The docs say
If set, the activity will not be launched if it is already running at
the top of the history stack.
But that statement seems to be burying a great deal of meaning beneath it. For instance someone online mentioned that the top activity may not be the same as the activity at the top of the task stack. I have no idea what all of that means. Hence my greater question: what are the implications of using FLAG_ACTIVITY_SINGLE_TOP?
It means if the activity is already up and you call it again you wont replace it with a new one and you wont create another one (which sometimes happens and is evident when you hit back and you see the same activity up), instead you will pull that one up.
So say you have 3 activities: A -> b -> c. You are in C and you came to it through A and then B. If you call to A, from C with the SINGLE_TOP filter your stack will look like A, C, B - if you started to hit the back button, you would go to C, then B. I could be wrong but thats how i believe it is. You can also pass in the CLEAR filter with it to erase the back stack and techniquely be started back at A with no stack in back of it, you would back straight out to home. - please correct if im inaccurate.
The following is an answer I read online. It is not a complete answer to the question you are asking, so I am really hoping someone else can add a lot more meat to it.
If an instance of the activity already exists at the top of the
current task, the system routes the intent to that instance through a
call to its onNewIntent() method, rather than creating a new instance
of the activity. The activity can be instantiated multiple times, each
instance can belong to different tasks, and one task can have multiple
instances (but only if the the activity at the top of the back stack
is not an existing instance of the activity).
For example, suppose a task's back stack consists of root activity A
with activities B, C, and D on top (the stack is A-B-C-D; D is on
top). An intent arrives for an activity of type D. If D has the
default "standard" launch mode, a new instance of the class is
launched and the stack becomes A-B-C-D-D. However, if D's launch mode
is "singleTop", the existing instance of D is deliverd the intent
through onNewIntent(), because it's at the top of the stack: the
stack remains A-B-C-D. However, if an intent arrives for an activity
of type B, then a new instance of B is added to the stack, even if its
launch mode is "singleTop".
Source

Prevent DialogFragment 'A' reappearing when selection made on DialogFragment 'B'

I have a DialogFragment, call it A, which presents an option that leads to a second DialogFragment, B, being displayed. B provides further options.
The functionality I require is as follows:
Making a selection in A leads to B being displayed (as stated above).
If the user hits back while B is being displayed, A should be resumed into view.
If the user makes a selection in B, then B should dismiss and A should not reappear.
In A, inside an onItemClick() handler I cause B to appear using:
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(DialogFragmentA.this);
transaction.addToBackStack("transaction_label");
DialogFragmentB dialogFragment = DialogFragmentB.newInstance( ...some args here...);
dialogFragment.show(transaction, "frag_B");
I call .addToBackStack() as I understand this will cause the back key to pop and reverse the transaction. That is, replace B with A again.
So far, requirements 1 and 2 are met.
B makes use of AlertDialog.Builder. A positive button is used with listener. When that positive button is pressed, I want requirement 3 to be met. That is, B should dismiss and A should not reappear. But what actually happens is A appears again.
I am assuming here that within the implementation of AlertDialog's positive button is a call to dismiss() which causes the back stack to be popped, resulting in A appearing again. Is this the case?
What I have tried to do is, within the positive button's onClick(), is to obtain the FragmentManager and call .popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE). But this has no apparent effect; A continues to appear. Using popBackStackImmediate() has no effect either.
Is this perhaps because the event loop has already somehow committed to popping the back stack by the time the positive button listener's onClick() executes?
I'd be grateful for an explanation for what is occurring and how I can make it work as intended.
I think I have nailed it, helped by the question and answer here.
The key point is that the sequence of transactions is nothing -> A -> B. I want to pop the back stack to the point where I have nothing. Therefore, as well as calling .addToBackStack() for the A -> B transaction, I also need to call .addToBackStack() for the nothing -> A transaction. Then, all of those transactions can be popped off the back stack again to get back to the point of nothing: The first pop does B -> A, then the next pop does A -> nothing.
For both transactions I am now calling .addToBackStack("some_transaction"). In B's positive onClick(), I call manager.popBackStack("some_transaction", FragmentManager.POP_BACK_STACK_INCLUSIVE);. I might rethink the tags I'm giving to the transactions, but this is now working as I want it to.
So the basic point seems to be that, if you want it to be possible to go back to a DialogFragment but also go back to the point where that DialogFragment wasn't shown at all, you have to add the transaction that put it there in the first place onto the back stack, so you can pop back to that point.
I previously thought it would be possible to achieve what I wanted by only adding the A -> B transaction to the back stack, and then somehow completely purging (rather than popping) the back stack to prevent A appearing again once a selection had been made in B. But it seems that there is no way to actually 'purge' the back stack; you can only pop and reverse the transactions.

How do you skip parts of an Activity stack when returning results in Android?

I'm making an app which has a flow roughly as below:
User starts on the main screen with an empty list, hits menu, and goes to "add item." (Activity A)
User is given a new activity which allows them to specify search criteria, then hits "go" to do a search. (Activity B)
User gets a list of results, and can click on one of them to view more details. (Activity C)
User sees details of item, and can use a menu item to save it to their list in Activity A. (Activity D)
Right now, I am having each Activity call each other Activity for results, and then it is passing the result all the way back up the stack as it returns to Activity A.
Is there a way to jump this, since all I want is for a result in Activity D to get to Activity A directly?
Note that a user should still be able to navigate backwards (using the back button) through each activity, but if they explicitly save the item in Activity D, I want it to jump straight to Activity A.
I recommend just invoking the activities (not using the *ForResult) calls, then having activity D invoke Activity A with an INTENT_ADD_ITEM with data, then have Activity A add the item.
Hope this helps...
Just so that people can benefit from what I learned later...
The key to solving this problem is using flags with Intent, in this case using FLAG_ACTIVITY_CLEAR_TOP. Other flags are useful as well in controlling the flow of your UI.
It is a bad idea to try to solve this problem by chaining startActivityForResult() through activities. It means that it's difficult to change the flow of your application.

Categories

Resources