Post navigation

Handling back button press Inside Fragments

This is the fourth part of a 6 posts series on Fragment oriented application architecture. In the previous post I talked about Inter-Fragment Communication. In this part I am going discuss about elegantly handling back button press inside fragments in a fragment oriented application.

Android devices have a hardware back button which normally serves the purpose of going back through the screens/actions stack. Callback to a back button press event is received in the foreground Activity (onBackPressed() event callback) which can be overridden and handled.

But fragments do not get notified of a back press event. Suppose an Activity has a stack of 3 fragments. For a regular android user, normal expectation upon a back press would be to navigate back through the fragments. Of course, we would have added all the transactions to BackStack and now can enjoy the privilege of using popBackStack() method, but each fragment may have one or more special situations to consume back press. Handling back press for each of fragments inside the onBackPressed() call back of activity is the obvious choice but if the activity is supposed to host a good number of fragments, its onBackPressed() callback is likely to get cluttered and messed up.

For instance, in a ListFragment, a list row might expand upon clicking and a subsequent back press requires to collapse the expanded row.

This mess would grow exponentially with an increase in number of Fragments and complexity of each of them. Not clean!

After hours of hit and trials, I’ve finally come up with a strategy to cleanly propagate the back press event down to fragments, and let activity handle it only if the event doesn’t get consumed at fragment level. Enters the BackHandledFragment! An abstract base class that conveniently provisions to let a child active fragment utilize back press events. Host activity keeps track of its active fragment and in its onBackPressed() callback, it first checks if the active fragment requires to consume the back press, before handling it by itself.

This class declares an abstract method onBackPressed(). Child fragments would override this method to consume the back press and return a boolean to tell if back-press event was consumed.

onCreate() callback is enforcing the implementation of BackHandlerInterface, which has a method to mark a Fragment as the selected (or active) fragment of the hosting Activity. onStart() calls this communicator method to update selected fragment to current fragment (using this keyword).

Following would be the resulting back handling related code in the activity.

12 thoughts on “Handling back button press Inside Fragments”

Could you not acquire the currently active fragment inside the activity by popping the backstack? It seems weird to me to have the fragment tell its activity what is currently supposed to be the active fragment.
I haven’t tried this, but here’s how it could possibly work:

@Override
public void onBackPressed() {
//get the name from the topmost BackStackEntry which is also the fragment tag.
String fragmentTag =
FragmentManager.getBackStackEntryAt(FragmentManager.getBackStackCount()-1)
.getName();

By having the same name for the BackStackEntry as for its associated Fragment you could avoid the BackHandlerInterface altogether which I think is preferable since you generally don’t want Fragments interfering with the Activity’s interchanging of Fragments.
But like I said, it’s just a thought. I haven’t tried if this actually works.

I think this may run into difficulties if you have an application built around multiple fragments where only one or two is using the BackHandledFragment mechanism. After the BackHandledFragment has come and gone and another (non-BackHandledFragment) fragment has become active, the activity is left with a reference to a stale fragment in “selectedFragment”. This object won’t be recycled (memory leak) because an active Activity holds a reference to it, and this fragment’s onBackPressed() method will still be called, even though it is not the active fragment with which the user was interacting when pressing Back.

Chris’ proposal looks like a viable way to address those issues. No stale references to obsolete fragments, and only calling fragments if they are at the top of the stack.

Has anyone actually made this work? From what I’ve learned in my efforts, Activity.onBackPressed() doesn’t actually get called unless the back-press would result in Activity.finish() being called. In other words, when a back-stacked fragment is on the top of the stack, Activity.onBackPressed() does _not_ get called as Android sends the event to the fragment manager instead, so this mechanism can’t forward that event to the fragment.

A very great idea and it works effectively. One thing I am stuck at is.
Now when I press the back button in the fragment I come back to the activity(mission 1 complete)
Now in the activity when I press back button, the same activity is called again and again. What i want to do is when the context is my current activity and when I press back button something else should happen. It would be really great if anybody could provide the solution to this. Thanks