I would like to present a number of flaws in the UAMP architecture, and based on them, present an alternative architecture.
In the docs, it advises to use UAMP as the basis for a music player app. I found this to be an awkward task because of several architecture characteristics of UAMP:
It uses design patterns that are either not mentioned or are discouraged in the architecture docs (e.g. LiveData<Event> for navigation, exposing val data = MutableLiveData() instead of wrapping that in val data: LiveData<T> get() = _data, using java.net.URL to read the JSON catalog instead of Retrofit, etc.)
It does not use design patterns that are mentioned in the architecture docs (e.g. using the Navigation Component, having a repository mediate remote and local data sources, caching network response for offline use, storing recent song in Room instead of SharedPreferences, etc.)
A significant part of the media logic is coupled to the UI-related code (e.g. LiveData<Event> hooked up to the complex BrowseTree; exposing mediaButtonRes: MutableLiveData<Int> from the ViewModel instead of isPlaying: LiveData<Boolean>, and allow the UI to do with isPlaying what it needs - there are a number of other examples of coupling, etc.)
There is a significant amount of UI-related code
There is media player functionality that some may consider specific to the use case UAMP decided to mimic (e.g. buildPlaylist() in MusicService.kt)
There are prominent media player features that are completely missing - some of which have been in TODO.md for quite some time (e.g. having widgets for adjusting the playback progress (e.g. Slider, +/-30 sec buttons), ability to build custom queue from hand-picking songs and add or remove songs from the queue without rebuilding it from scratch, ability to adjust playback speed)
Because of this (among other reasons), the already complex task of creating a media app was further complicated by having to "cut away" the extraneous parts of UAMP - the parts that do not contribute to the inherent functionality of a media player - as well as add the basic features that were missing (a process I am still arduously going through, with little success). On a first iteration, the extraneous code totaled approximately 800 lines of code. This was on top of the fact that I was trying to integrate player functionality to an already existing app, which meant removing the above-mentioned infrastructure code from UAMP, and implant UAMP in my existing app (it has not been a clean surgery). I turned to UAMP for this integration, because I knew that I could rely on UAMP to flawlessly implement the player functionality, so that I can have one less part of the app to worry about looking for bugs in. I was (and still am) hesitant to use an open-source player, because I can't be guaranteed that there likely won't be bugs from their implementation. Unfortunately, it is starting to look like using UAMP was the long shortcut.
It seems that UAMP falls in the no-man's land between a showcase app like Sunflower - which tries to be a full example of correct app architecture - and a codelab - which tries to be as bare-bones as possible, writing just enough to get across the point while still leaving you with reusable code at the end, and leave more advanced features for your curiosity in an alternative feature branch.
I would like to request a shift in architecture philosophy, from creating an app that people will copy and start writing their own app based on, to the more common codelabs-style barebones example. To date, (according to my results on Active Forks Finder) there are only 17 forks of this projects whose commits ahead of this project are non-zero (see table below), which seems to indicate that people are not deriving value from UAMP in the way that the docs suggest (viz. basing their media app on UAMP).
Besides for fixing the above issues, an actionable way to make it simpler is to remove the hierarchy of Albums -> List of albums -> List of songs -> Song NowPlayingFragment, and replace it with a predefined list of songs in a RecyclerView (they could be hardcoded or retrieved from an API, though the former would be more focused on music playing). The main activity would open up to that list of songs. This would remove the need for BrowseTree, all of the logic regarding isBrowsable, rootMediaId, the MediaItem abstraction, and probably all of the logic for navigation with LiveData<Event> (because there would only be two simple top-level destinations: list of songs and NowPlayingFragment).
I hope these issues appear cogent, and that my request for a change in architecture philosophy is considered. Given my use case, it would suit me more if the change happened in the direction of a codelab style, but if it took a step towards becoming more of a showcase app, I would be pleased all the same.
I would like to present a number of flaws in the UAMP architecture, and based on them, present an alternative architecture.
In the docs, it advises to use UAMP as the basis for a music player app. I found this to be an awkward task because of several architecture characteristics of UAMP:
LiveData<Event>
for navigation, exposingval data = MutableLiveData()
instead of wrapping that inval data: LiveData<T> get() = _data
, usingjava.net.URL
to read the JSON catalog instead of Retrofit, etc.)LiveData<Event>
hooked up to the complexBrowseTree
; exposingmediaButtonRes: MutableLiveData<Int>
from theViewModel
instead ofisPlaying: LiveData<Boolean>
, and allow the UI to do withisPlaying
what it needs - there are a number of other examples of coupling, etc.)buildPlaylist()
inMusicService.kt
)Slider
, +/-30 sec buttons), ability to build custom queue from hand-picking songs and add or remove songs from the queue without rebuilding it from scratch, ability to adjust playback speed)Because of this (among other reasons), the already complex task of creating a media app was further complicated by having to "cut away" the extraneous parts of UAMP - the parts that do not contribute to the inherent functionality of a media player - as well as add the basic features that were missing (a process I am still arduously going through, with little success). On a first iteration, the extraneous code totaled approximately 800 lines of code. This was on top of the fact that I was trying to integrate player functionality to an already existing app, which meant removing the above-mentioned infrastructure code from UAMP, and implant UAMP in my existing app (it has not been a clean surgery). I turned to UAMP for this integration, because I knew that I could rely on UAMP to flawlessly implement the player functionality, so that I can have one less part of the app to worry about looking for bugs in. I was (and still am) hesitant to use an open-source player, because I can't be guaranteed that there likely won't be bugs from their implementation. Unfortunately, it is starting to look like using UAMP was the long shortcut.
It seems that UAMP falls in the no-man's land between a showcase app like Sunflower - which tries to be a full example of correct app architecture - and a codelab - which tries to be as bare-bones as possible, writing just enough to get across the point while still leaving you with reusable code at the end, and leave more advanced features for your curiosity in an alternative feature branch.
I would like to request a shift in architecture philosophy, from creating an app that people will copy and start writing their own app based on, to the more common codelabs-style barebones example. To date, (according to my results on Active Forks Finder) there are only 17 forks of this projects whose commits ahead of this project are non-zero (see table below), which seems to indicate that people are not deriving value from UAMP in the way that the docs suggest (viz. basing their media app on UAMP).
Besides for fixing the above issues, an actionable way to make it simpler is to remove the hierarchy of Albums -> List of albums -> List of songs -> Song
NowPlayingFragment
, and replace it with a predefined list of songs in a RecyclerView (they could be hardcoded or retrieved from an API, though the former would be more focused on music playing). The main activity would open up to that list of songs. This would remove the need forBrowseTree
, all of the logic regardingisBrowsable
,rootMediaId
, theMediaItem
abstraction, and probably all of the logic for navigation withLiveData<Event>
(because there would only be two simple top-level destinations: list of songs andNowPlayingFragment
).I hope these issues appear cogent, and that my request for a change in architecture philosophy is considered. Given my use case, it would suit me more if the change happened in the direction of a codelab style, but if it took a step towards becoming more of a showcase app, I would be pleased all the same.
Forks with commits ahead