Transaction BackStack and its management

This is second part of a 6 posts series. In the first post I talked around basics of fragment oriented architecture. From this post onwards, I’ll be talking about it’s implementation details.

(Sample application’s source code and README)

In this part I am going to talk about Transaction Backstack and few related methods that can be used frequently.
Transaction BackStack has often been misinterpreted as backstack of fragments. FragmentManager inside an activity deals with fragment-transactions rather than with fragments. An entry into this backstack is a ‘fragment-transaction’ which can be composed of one or more operations involving fragment(s). Reverting this would revert back all these operations together.

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(restId, fragmentA);
ft.replace(fragmentB);
ft.commit();

Above, is a single transaction clubbing multiple operations.

Transactions do not get added to back stack by default. An explicit call to addToBackStack(String tag) before committing it, would add it to BackStack. The ‘tag’ string would be an identifier of the transaction and can be used to refer back to it later.

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(resId, fragmentA);
ft.replace(resId, fragmentB);
ft.addToBackstack("tag");
ft.commit();

Reverse of this method is popBackStack() and its following variants.

  • popBackStack() pops the top transaction from stack.
  • popBackStack(String tag) pops transactions until the transaction with supplied ‘tag’.
  • popBackStack(String tag, int flag) pops till the transaction with mentioned ‘tag’ if flag is POP_BACK_STACK_INCLUSIVE, or else 0 can be used.
  • popBackStack(int id, int flag) pops till the transaction with mentioned ‘tag’. Id is the identifier returned from FragmentTransaction.commit().

Above methods actually just enqueues a transaction in FragmentManager’s ToDo queue and would come in effect only when application returns to its event loop. In order to enforce the transactions to come in effect immediately, there are following additional variants with same effects respectively.

  • popBackStackImmediate()
  • popBackStackImmediate(String tag)
  • popBackStackImmediate(String tag, int flag)
  • popBackStackImmediate(int id, int flag)

How transaction backstack is not fragment backstack, is demonstrated in the sample app in ‘Back Stack Handler’ section as follows. If you press ‘Go to step 1’ button, the transaction adds  FirstStepFragment and commits. Thus, on popBackStack() from this fragment, app would remove FirstStepFragment and get back to ‘BackStackHandlerFragment’.

// TO ADD A FRAGMENT.
@Override
public void addFragment(BaseFragment baseFragment, boolean withAnimation) {      
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    if(withAnimation) {	
        ft.setCustomAnimations(R.anim.fragment_slide_in_left, R.anim.fragment_slide_out_left, R.anim.fragment_slide_in_right, R.anim.fragment_slide_out_right);    
    }
    ft.replace(R.id.home_frame_layout_for_fragments, baseFragment, baseFragment.getTagText());
    ft.addToBackStack(baseFragment.getTagText());
    ft.commit();
}

Instead, if you press ‘Go to step2’ in BackStackHandlerFragment, a transaction would occur with two ‘add’ operations, adding both FirstStepFragment and SecondStepFragment clubbed in a single transaction. So, a single popBackStack() call from SecondStepFragment would remove both these fragments and app would get back to BackStackHandlerFragment.

// TO ADD MULTIPLE FRAGMENTS CLUBBED INTO ONE TRANSACTION.

@Override
public void addMultipleFragments(BaseFragment[] baseFragments) {
    // Initialize a Fragment Transaction.
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
		
    // Record all steps for the transaction.
    for(int i = 0 ; i < baseFragments.length ; i++) {
        ft.setCustomAnimations(R.anim.fragment_slide_in_left, R.anim.fragment_slide_out_left, R.anim.fragment_slide_in_right, R.anim.fragment_slide_out_right);
        ft.replace(R.id.home_frame_layout_for_fragments, baseFragments[i], baseFragments[i].getTagText());
    }
		
    // Add the transaction to backStack with tag of first added fragment
    ft.addToBackStack(baseFragments[0].getTagText());
		
    // Commit the transaction.
    ft.commit();
}

Another requirement could be of clearing the back stack.

// CLEAR BACK STACK.
private void clearBackStack() {
    final FragmentManager fragmentManager = getSupportFragmentManager();
    while (fragmentManager.getBackStackEntryCount() != 0) {
    	fragmentManager.popBackStackImmediate();
    }
}

The above method loops over all the transactions in the backstack and removes them immediately one at a time.

You may want to take a look at the following as well while dealing with transaction backstack management.

In the next post of this series I’ll be talking about Inter-fragment communication.

6 thoughts on “Transaction BackStack and its management

  1. Shouldn’t “popBackStack(int id, int flag) pops till the transaction with mentioned ‘tag’” say “…with mentioned ‘id’”?

  2. Nope. There is another variant of popBackStack, ‘popBackStack(String tag, int flag)’, for popping till ‘tag’

  3. Nice post :) Just one question: under what circumstances should we use ft.add() vs ft.replace() while the fragments are added to backStack? I know that if we use ft.add() and we press the back button the lifecycle methods won’t be called, while for the ft.replace() the lifecycle methods will be. But I want to know what method it’s the best approach to use in general. Thanks!

  4. Pingback: Is this the right way to clean-up Fragment back stack when leaving a deeply nested stack? - ExceptionsHub

Leave a Reply

Your email address will not be published. Required fields are marked *