Request, parsing & persistence of a Json flux to display a list of albums using clean architecture & MVVM pattern.
The main inspiration for my application comes from the design of Shakib Ali intituled Workout & Fitness App. I really liked the listing page so I tried to reproduce it.
I also had inspiration from the two following designs :
- Planet of events design mobile app from Vadim Bondarenko and Yakovlevv Design.
- Culttrip from UGEM Design
To be able to provide a great user experience when loading or when an error occurred while fetching the data, I browsed some really great projects, for example :
- Error Illustrations - Empty States Vol 02 from Nimasha Perera
- DAY 62-empty state from Shangnan Zhang
To find the logo to display the current state of my adapter list, I used the website Undraw.
Last, but not least I have found my launcher icon on pixabay.
For, my implementation I used one single Activity with multiple fragments. To go trough the fragment I used the navigation library of Jetpack. We have two fragments :
- ListAlbumFragment, which correspond to the current list of albums
- DetailAlbumFragment, which is the detail of a specific album that we can reach by clicking on an item of the previous Fragment
For the ListAlbumFragment, I tried to provide a nice & efficient user experience, thanks to different states. As soon as, you launch the application a loading screen will appear to help the user to be patient, If you launch the application without having internet, if you have a timeout or a server issue. You will end with a screen which gives you a way to relaunch the request.
If you have internet and the server response well, you will have a fadeOut animation hiding the loading screen and thus reveal the list of images.
The loading of the items is progressive, I load the data by a batch of 15 items and as soon as you reach a certain amount of items the next batch is fetched. The progression and scrolling position are saved by using the SavedStateHandle class.
As soon as you clicked on an item for the previous list you end in the DetailAlbumFragment, which display the detail about the Album.
In the same way of the ListAlbumFragment, we save the albumId with a SavedStateHandle to be able to recover inside the DetailAlbumFragment with the good item if the application has been killed in background.
For this test, I tried to use a clean architecture following this example and this article. The main objective is to be able to have a great separation of concerns and thus improve the testability of the code. To do so, I have divided this project in 3 layers :
- Domain Layer: The domain package, which is responsible for handling pure Business Logic and defining Entities representing our business models.
- Data Layer: The Data modules will implement the interfaces defined in the Domain layer.
- Presentation Layer: The presentation module which contains our activities/fragments. For the presentation layer I used an MVVM architecture.
- Retrofit: A type-safe HTTP client for Android. I used a moshi converter. I tend to use moshi over gson or jackson since I saw that talk. Moshi seems to better handle accent and error than Gson and is much smaller than Jackson.
- For my database, I chose Room for the efficiency to handle entities and database access.
- Coroutines: Light-weight thread implementation. I like the readability and the simplicity of coroutine.
- To load images, I picked Coil, which released the 1.0 recently. I liked it, because the library is smaller than Glide & Picasso & the library is backed by Kotlin Coroutines.
- Koin: I used Koin for dependency injection.
- I used Barista to ease my UI test writing.
- I had issues with Android KitKat, the library OkHttp dropped the support of Android 4.4 in the latest version. So to be able to have Retrofit working I needed to downgrade & force the version of the OkHttp library.
- I encounter some issues while testing the ViewModels classes, I tried to make an implementation based on Mockito. The result was working, but the tests were flaky, they were failing half the time. The solution was pretty simple, by adding the CoroutineDispatcher used by the ViewModel in the constructor, I was able to use my own Dispatcher for the test and thus succeed to execute more easily and remove Mockito.
- I was using Koin 2.2.0 in the project. This version has been released the 13 October, I tried to add a SavedStateHandle to my ViewModel using the new syntax (they advise to use get() as usual), but I encountered an exception by doing so. So I downgraded to the version to the 2.1.6 where the syntax is a bit different, but works pretty well !
During the test, I read several interesting things like :
- The next release of the RecyclerView library (1.2.0-alpha6 for now) will be able to restore the scrolling position directly, no need to handle it by ourselves.
- An article by the developer of Barista to explain his adventure with Android UI tests.