Let me tell you a story about Joe Somebody an android developer at MyLittleZoo Inc. and how he walked through the hell while trying to create reusable RecyclerView Adapters with different view types and how he finally managed to implement reusable Adapters painlessly.
Once upon a time Joe Somebody, an android developer, was working for a young startup called MyLittleZoo Inc. This startup was selling stuff for pets online. Joe’s job was to build and maintain a native android app which basically offers the same functionality as the online shop (website). So 90% of the android app he had to develop just displays a list of items in a RecyclerView. The first version 1.0 should just display a list of Accessories. Joe implemented an AccessoiresAdapter which displays a list of accessories, but special offers for accessories are displayed by using item_accessory_offer.xml while the item_accessory.xml is used to display any normal accessories item. So the Adapter has two view types. A view type allows you to inflate different xml layouts for different items in the adapter. Internally a view type is just a unique id, an integer. So Joe’s AccessoiresAdapter implementation looks like this:
So far so good, MyLittelZoo android app 1.0 was published on Play Store. Everything was cool.
Then MyLittelZoo grew, so did the app. Joe had to implement a new starting Activity where different items could be displayed: NewsTeaser should now be displayed together with Accessories. Since HomeAdapter should display Accessories as well he decided to reuse AccessoriesAdapter by inheriting from that one:
Also a new Activity just displaying some short tips about pet food should be implemented. Hence Joe implemented PetFoodTipAdapter:
His project manager was happy since he was able to deliver in time. MyLittelZoo 2.0 was released on Play Store successfully.
A few weeks later product manager came to Joe and told him that business hadn’t developed as expected. To earn money the company decided to sign a contract with a big advertisement company. The advertisement company could display banners in MyLittleZoo android app. In other words: they sold their soul to the devil. Joe’s job was to include advertisement banner in the app by using a provided advertisement sdk. The clock ticked, the company needed money (revenue from advertisement). The app update had to be published as soon as possible. Since advertisement banner should be displayed along with other items in a RecyclerView Joe decided to create a new base adapter class called AdvertismentAdapter:
From then on, every other Adapter extends from AdvertisementAdapter:
- AccessoiresAdapter extends AdvertisementAdapter
- HomeAdapter extends AccessoiresAdapter extends AdvertisementAdapter
- PetFoodTipAdapter extends AdvertisementAdapter
Version 3.0 was published on Play Store with advertisement banner everywhere. Once more product manager was happy with Joe’s work.
A half year later again the product manager knocked on Joe’s door to tell him that things had changed. Surprisingly it turned out that the user of MyLittleZoo android app didn’t liked the blinking advertisement banner introduced in version 3.0 and the app got huge negative reviews on play store. User sessions dropped dramatically and company didn’t earn money anymore. But MyLittleZoo couldn’t simply remove advertisement from the app since they have signed a valid longterm contract with the devil, ehm I mean the advertisement company of course.
Then the smart marketing guy at MyLittleZoo had the brilliant idea to launch a second app with just displaying NewsTeaser and PetFoodTip in a RecyclerView. No advertisement, no offers. The plan was to regain the confidence of the users. Furthermore, product manager told Joe that the app had to be published within next two days because at the upcoming weekend was a big pet fair where the app should be presented. Joe thought it was doable. He already had the xml layouts for NewsTeaser and PetFoodTip and the adapter were already implemented. So all Joe had to do is to move that into an android library to share them between original MyLittleZoo app and the new advertisement free app.
Joe was about to start moving things into the library when he realized the mess he was facing: Do you remember the inheritance hierarchy of the adapters?
- Every Adapter extends from AdvertisementAdapter. But no advertisement should be displayed in the new app. Moreover, the provided advertisement sdk to display banners is really buggy, causes memory leaks and crashes quite often. Even if no advertisement banner was displayed the advertisement sdk did a lot of crap in the background. Therefore, including the advertisement sdk in the new app was not acceptable.
- There is no adapter that he can reuse that can display NewsTeaser (part of HomeAdapter) and PetFoodTip (part of PetFoodTipAdapter). What should Joe do? He could create a new Adapter called NewsTipAdapter that extends from HomeAdapter and then he would had to add the PetFoodTip as new view type. But that would mean that he would had two Adapters to maintain for the same view type PetFoodTip.
Welcome to the adapter hell Joe!
Oh boy, Joe was depressed. Then panic followed depression. How should he fix that? How should he fix that without having to fix it again a month later when a new feature (a new view type) must be implemented?
So Joe started to write down his requirements on a whiteboard. But not a single good idea came out. He was so sad, he thought back to the days when he was a little child. How easy was life during childhood. The only thing he had to worry those days was to clean up his room after he had finished to play Lego. Lego? Wait, wait, wait! Joe had a brilliant idea: What he really needs is to built adapter like building a Lego house: Take an empty fundament and then stick together that Lego pieces you really need. If you need a window in your Lego house, take a window piece. If you need a roof slope take the corresponding Lego piece. If your Lego house needs a backyard, take a Lego flower.
Damn, and then he get the overall picture:
Favor composition over inheritance
So many times he had agreed on “Favor composition over inheritance” while discussing with other developers. Until now it was just a good slogan but he never really have build something according this principle. So an empty Adapter is the fundament. ViewTypes are the reusable components (Lego pieces).
So Joe started to define the reusable Lego pieces like NewsTeaserAdapterDelegate and PetFoodTipAdapterDelegate:
And then he took the fundament, an empty adapter, and put the Lego pieces on top of it to create the NewsTipAdapter which will be used in the new app:
I guess you get the point. Instead of inheriting Joe had defined a delegate for each view type. Each delegate was responsible for creating and binding a view holder. As you have noticed in the code snipped above, there is a lot of boilerplate code to write. Joe found a smart plugin solution for that:
The idea is to register AdapterDelegates to an AdapterDelegatesManager. The AdapterDelegatesManager internally has the logic to determine the right AdapterDelegate for the given view type and to call the corresponding delegate methods. So applying that to NewsTipAdapter the code looks like this:
I guess you can imagine how other adapters of MyLittleZoo app looks now. There is an AdvertisementAdapterDelegate, NewsTeaserAdapterDelegate, PetFoodTipAdapterDelegate and AccessoryAdapterDelegate. From now on adapters can be composed with that view types (AdapterDelegates) that are really needed. Another advantage is that you also have moved out the functionality of inflating layout, creating view holder and binding view holder from one huge adapter class (spaghetti code? god object anti pattern?) into separated, modular and reusable AdapterDelegates. Have you noticed how slim adapters code looks now and that you have a separation of concerns that makes things more extendable and more decoupled? Another nice side effect is that more team members can work in parallel together on the same “adapter” without fearing complex merge conflicts because not everybody is touching the huge adapter file but rather team members can work on dedicated AdapterDelegate files simultaneously.
Joe was happy, the product manager was happy and the users of the app were happy. Everybody was happy. Actually, Joe was so happy that he had decided to put AdapterDelegates in an own library and open source it. All’s well that ends well.
You can find AdapterDelegates on Github and is available in maven central.
P.S. The AdapterDelegates library also provides a base class ListDelegationAdapter that already puts together RecyclerView.Adapter methods with AdapterDelegatesManager methods so that you can reduce the amount of writing boilerplate code even more:
Check out the library on Github for more details.
Disclaimer: Joe is not a real person nor is MyLittleZoo Inc. a real company. Both are creatures of my imagination. Please note also that the code snippets shown in this blog post may not compile. It’s kind of java alike pseudo code to give you an idea of how real code could look like.