Like most of its Android counterparts, Zumper’s consumer app retrieves data from the Internet to back nearly every single view. Generally, the protocol is simple: when a user visits a certain section, the Zumper app makes a network call in onCreate:
Note: our API layer is essentially a wrapper around Google’s Volley networking library.
In a perfect world awash with infinitely fast internet and herculean backend computational power, API requests like the above complete just as soon as they’re initiated; whatever we want to do in onResponse() ou onErrorResponse happens instantaneously.
Obviously, we don’t live in a perfect world and all of our apps have to cope with less than ideal network reliability and latency. For the most part, we can get away with asking our users to wait a tic for data to load from the network. In general, this strategy is low-risk; users are willing to sit tight for a few seconds before leaving. But what about those that do end up navigating away? This use case becomes a problem in the somewhat common case that you need to display a Fragment ou DialogFragment in one of your callback methods.
Let’s take a look at an example from Zumper’s consumer app. One of our main selling points is our in-app identity verification and credit report functionality, by which a user can obtain her credit report from her Android (or iOS) device. After obtaining a credit report, a Zumper user can access her score at any time, but only after creating a secure session:
From a networking perspective, the process looks relatively simple:
An excerpt from Zumper’s ViewCreditReportFragment.onCreate()
While the back and forth between the client and server is pretty speedy, the architecture above leaves us open to an IllegalStateException if the user does something that causes the containing ViewCreditReportFragment to save its state, like navigating away from the view. Why? When the app calls CreditAuthDialogFragment.show(), the childFragmentManager uses its checkStateLoss() method to ensure that the UI change (i.e. displaying the dialog window) will not be lost across Activity destruction and recreation1:
From FragmentManagerImpl.java
So, when does the FragmentManager save our ViewCreditReportFragment’s state? According to the documentation, state can be saved any time before onDestroy. Looking into the issue further, you’ll find a comment in the FragmentManagerImpl.saveAllState() method that indicates state is saved after pausing2. From a practical perspective, the safe assumption is that state is saved when a Fragment moves out of view3.
What can we do to avoid this crash? Based on the discussion above, when we want to commit a FragmentTransaction (e.g. when we want to call DialogFragment.show()), we need to make sure that our ViewCreditReportFragment hasn’t yet been paused. To this end, we’ve relied heavily on the RxJava and RxLifecycle libraries to ensure that we don’t commit FragmentTransactions after state is saved. The magic, so to speak, lies in the combination of RxJava’s Observable.compose() method with RxLifecycle’s RxLifecycle.bindFragment() method.
To incorporate this reactive functional wizardry (thanks Netflix!), we began rewriting our networking APIs to take advantage of Observable streams, RxJava’s basic building block. The transformation resulted in something like this:
To incorporate the RxLifecycle piece (shoutout to Trello for this one!), we retrofitted Zumper’s base parent Fragment to communicate its lifecycle via calls to a BehaviorSubject, the very same BehaviorSubject that is passed into RxLifecycle.bindFragment():
Now, whenever Zumper’s consumer app makes a call to the network within a Fragment, whether in a lifecycle method or elsewhere, we use our nifty Observable APIs and bind them with the lifecycle of the Fragment using compose():
This design pattern results in a syntactically concise and elegant solution to a ubiquitous problem. While we haven’t finished the migration quite yet, we’ve essentially eliminated IllegalStateExceptions where we’ve used this model, resulting in a more stable and fulfilling user experience. To boot, our adoption of reactive functional APIs has enabled more declarative and flexible code, especially where collection manipulation or multiple network calls are required in sequence. But that, my friends, is a story for another day.
Footnotes:
1. See Alex Lockwood’s post on the matter for more information on `FragmentTransaction`s, state loss, and `IllegalStateException`s.
2. State is saved after pausing in Android API 11 (Honeycomb) and above. Before that, state was saved before pausing.
3. There are exceptions, e.g. within a ViewPager.

A transplant from the legal world, Justin took an interest in web development during his stint as a litigation consultant in 2012. After a year of dabbling, he took his first job as a professional Android developer, having no prior Android experience. Times have changed, though, and Justin’s now part of Zumper’s two-man Android development team, building a mobile client that’s garnered both the Editors’ Choice and Top Developer distinctions.










