Introducing Graywater, Tumblr’s framework for decomposing complex items in a RecyclerView list in order to improve scroll performance, reduce memory usage, and lay a foundation for a more composition-oriented approach to building lists. With Graywater, the app now scrolls faster and crashes less often, and it also gives us a solid foundation for building new features faster and better than before.
On screens that display posts, such as the dashboard, the Tumblr Android app customizes one adapter across multiple screens. This approach results in a complex adapter, and over time, our previous solution became difficult to manage and hard to reason about since there was no consistent place for screen-specific behavior.
Furthermore, each post type had its own layout and viewholder, which meant that once a user encountered a post type they hadn’t seen on that screen before, the entire post had to go through the inflate, layout, and draw process. Once offscreen, the post would take up large chunk of memory in the RecyclerView pool.
Graywater solves this by rendering only the parts of a post that are visible and reusing the parts of a post that appear in other posts, such as the header and footer. By breaking up a large post into smaller components, the UI thread has to do less on each scroll. Even though there are more view types, each individual view type is smaller, so memory usage is lower.
For example, a photoset post may be composed of ten photos, one after another. In the previous architecture, a photoset layout with headers and footers would be inflated and the photo views added in afterwards. If the viewholder is recycled and the next photoset post only has one photo, the extra photo views are discarded. With Graywater, each individual photo view is recycled separately, which allows RecyclerView to reuse the photo views that appeared earlier in the photoset.
Graywater differs from other RecyclerView libraries by being small (a single file!) and flexible enough to work within your model architecture. For libraries like Epoxy and Groupie to accomplish sub-item recycling, complex items like posts need to be decomposed into smaller viewmodels beforehand. For Litho to flatten view hierarchies and perform fine-grained recycling, existing XML layouts need to be converted to layout specs.
By converting to Graywater, we’ve been able to reduce OutOfMemory errors by 90% and dramatically improve scroll performance. It is now much easier to add new item types that are composed of preexisting post components. We have also migrated screen-specific logic to the screen itself by injecting the customized component into the adapter. By open-sourcing Graywater, we’re hoping the Android community will achieve similar performance and architecture gains, and we’re excited to hear what the community builds next!