The idea that an Android mobile app and an Android TV app should be developed separately from one another is a common misconception. Separating the Android TV development process from the mobile process can create confusion, duplicate feature requests, and fragmented teams stepping on each other’s toes. While the designs for these platforms differ, a lot of code from a mobile app will be common to the TV app and vice versa. For example, the database models, network stack, authentication flow, business logic, and media players should be shared. For an MVC or MVVM architecture, that would mostly involve the Model and the stack used to build and retrieve the Model. For VIPER, the Entities and Interactors should be shared between mobile and TV. The ability to share code across platforms is great news for any Android app that wants to expand onto the Android TV platform. It significantly lowers the cost of development because resources can be shared and there’s no need to start from scratch. Also, if an app wants to reach Fire TV users, Fire TV is merely a fork of Android Lollipop so besides some Google specific services, apps built for Android TV work on Fire TV and vice versa.
Google provides useful step-by-step instructions for developing Android TV apps. Notice that there’s zero mention of how to build the video player for a TV app or how to add a database to the app. Those are topics common to Android in general so repeating the documentation would be redundant. Instead, the guide explains how to handle the differences from mobile Android, UI and UX. User interaction shifts from a touch screen to a remote control. A TV app will have to handle key events and focus properly at the view level. This doesn’t change how Android views are implemented fundamentally, it merely adds some extra listeners and events. Furthermore, the key events that one would expect to perform as a click action actually do trigger a click so most UI elements can use a click listener like normal Android, as this code snippet from AOSP shows. In addition, the user experience changes from inches to feet. Adding some extra views to an existing app should only require a feature request, not a restart.
For code clarity, it helps to use a consistent strategy to separate TV views from mobile views. For example, both mobile and TV might show a “Featured” page. One option is to split them into separate packages but if they’re both called FeaturedFragment, then auto-import won’t know which one to grab. Therefore, even if separating the views into distinct packages, it helps to use “TV” in the name like TvFeaturedFragment and FeaturedFragment. This also creates better code-readability by placing the developer into a TV mindset right away. As always, consistency creates readability so even TV views that don’t conflict with mobile names can benefit from this naming pattern.
To avoid breaking the DRY principle, a strategy should be developed for sharing as much common code as possible between the platforms. Both TvFeaturedFragment and FeaturedFragment need to interact with some common objects to retrieve network info, RemoteFeaturedDataSource, or provide a model of a featured item, FeaturedItemModel. The data sources and models are shared classes because they perform identical functions for both platforms. Now, an update to a new networking library won’t require two distinct changes for both TvFeaturedFragment and FeaturedFragment.
Sharing common objects exemplifies a traditional “has-a” relationship where both fragments have a data source. Some situations lend themselves more naturally to an “is-a” relationship and inheritance. Consider adding an analytics manager that needs to fire off for lifecycle events. It could be plugged into both TvFeaturedFragment and FeaturedFragment, which creates duplicate calls in several places. Later when requirements inevitably change, adjusting the parameters passed in the calls or removing the calls from a couple lifecycle methods would have to happen in both fragments. Instead, a BaseFeaturedFragment could implement the analytics calls in one place. Now TvFeaturedFragment and FeaturedFragment merely extend BaseFeaturedFragment to meet the necessary analytics requirements. This keeps the analytics code in one place allowing for easier maintenance. In fact, both fragments need to fire off the same data source so the RemoteFeaturedDataSource call can be moved into the BaseFeaturedFragment too. As a reminder, the only difference between TV and mobile is the UI and the UX, everything else should be the same.
However there’s a difference between what should be and what is. “Everything else should be the same” should be taken with a grain of salt. Such an all-encompassing statement deserves some nuance. Some business logic might differ. The analytics manager might expect entirely different parameters from the different platforms. Once the mobile and TV app requirements diverge too much in any one area, let it go. Split out the diverging pieces into SomeFeature and TvSomeOtherMarginallyRelatedFeature classes. It’s a lot cleaner to make a distinct split rather than shoehorn several conflicting pieces into shared classes. If every other method is changing its functionality based on an isTv() boolean method, that’s a red flag that a class should be split into separate classes. In general though, mobile and TV views can share most code with common objects and inheritance.
Also, personal experience suggests that interfaces for views usually require a TV and a mobile version. A lot of architectures rely heavily on interfaces, which is good, but trying to share interfaces can create significant chunks of dead code. Specifically, problems happen when sharing interfaces between views since TV and mobile views do diverge. If a view is implementing an interface but only implementing half the methods, then it clutters the view with empty methods that it has to override. Essentially, if an architecture tightly couples views to interfaces, then the interface should split between mobile and TV when its view splits to avoid creating a lot of empty, unused methods.
Overall, a mobile and TV app should live in harmony with a lot of shared business logic and models. Managing both TV and mobile together can help fight the misconception that the apps are distinct beasts. Besides different user interfaces and unique user experiences, both TV and mobile apps are Android to the core. Be together. Not the same.