- Introduction
- Final Product
- How To Run The App
- Development
- Aditional reading on Android MVVM
- Questions?
- Resources That Made This Project Possible
Weather in Tamriel is a demo app that serves two purposes. First, It allows me to demonstrate some of what I've learned in my 3+ years of professional android development. Second, this repo serves as an example MVVM architected android app anyone can read through to better understand both Android development in Kotlin and the MVVM pattern. It was originally derived from a simple weather application that I built while reading Antonio Levia's Kotlin for Android Developers. I took that idea and decided to add more features while also injecting an overall design architecture and redesigning the way everything works together. You'll notice the app is themed like that of Tamriel from the Elder Scrolls series... that part was just for fun! Praise Talos.
Here's the gif of the final product. I encourage you to read more about the app's development, but if you just want to see the final form, I totally get it.
To make sense of what your seeing, consult the below tables for how dates in Tamriel work, courtesy of The Unofficial Elder Scrolls Pages.
Month on Tamriel | Month on Earth |
---|---|
Morning Star | January |
Sun's Dawn | February |
First Seed | March |
Rain's Hand | April |
Second Seed | May |
Midyear | June |
Sun's Height | July |
Last Seed | August |
Hearthfire | September |
Frostfall | October |
Sun's Dusk | November |
Evening Star | December |
Day of week on Tamriel | Day of week on Earth |
---|---|
Sundas | Sunday |
Morndas | Monday |
Tirdas | Tuesday |
Middas | Wednesday |
Turdas | Thursday |
Fredas | Friday |
Loredas | Saturday |
The app is not going to be deployed on the google play store for various reasons including licensing (I don't work for Bethesda yet) and API request limits (I don't own the API's being utilized). What this means is that to run the app you'll need to manually build the APK yourself. Simply download android studio if you don't already have it installed, clone the repo, and build the project APK to whichever device/emulator you wish! For more on how to run and deploy a project with Android Studio, I encourage you to read the following page of the Android developer documentation.
If you wish to use Docker to build the APK (I highly recomend you do so), feel free to check out the docker-based-install branch and browse through the readme over there. It will contain all the instructions needed to build the app in a container!
This project took on a very agile-like workflow. I consistently produced iterations which featured fully functioning products as I worked towards my final deliverable. This iterable approach allowed for faster-developed prototypes and the ability to make changes to my development process with ease. Unfortunately, as the only contributor, I wasn't able to utilize any sort of feature-branch-workflow nor did I write any unit test (which is part of the greatness of the MVVM architecture, more on that later...) I've included gifs/pictures of every "iteration" I developed. Each of these stages can be found by scraping through the commit history of this repo. You can think of the below images as iterations 0-5 in the order they are displayed.
I once read a quote in another developers blog post that stuck with me. It basically stated, "You know an app has been thoughtfully designed when something breaks, and as a developer, you know exactly where to go to fix it." That is the essential idea behind carefully implementing an application architecture. If you're a longtime android developer, you probably know the pitfalls of developing a project without a clear architectural pattern. Architectural design is relatively new in the Android world and allows us to define how pieces of the app work together/how much they know about other components. The MVVM (model-view-viewmodel) is similar to the more popular MVC (model-view-controller) or MVP (model-view-presenter) patterns you might find in other areas of software development. With MVVM your ViewModel exposes a stream of updates to the view whose only responsibility is to render a given state. Because of this reactive approach, the ViewModel doesn't need a reference to the view like you would need in an MVC/MVP pattern and furthermore all of those ugly interfaces are dropped completely. To further understand MVVM, consider the role of each component
- Model - the data section of our app. A housing for all data that needs to be manipulated and displayed
- View - the UI portion of the app. In charge of displaying any events, it observes from the ViewModel.
- ViewModel - the connection between the model and the view. Handles communication with repositories for aggregating data into models and telling the view about any updates it needs.
Here's a nice graphic that shows the relationship of each component
The projects source file directory is structured in a way such that it makes it easy to find the code for each component.
├── src/main/java/weatherintamriel
│ ├── api
│ ├── component
│ ├── **model**
│ ├── module
│ ├── util
│ ├── **view**
│ └── **viewmodel**
Android's architecture components provide a nice layer of abstraction over the underlying mechanisms that make the MVVM model work. LiveData and ViewModel, in particular, are a huge help in implementing a system that's Lifecycle aware. That's right, no storing and restoring data in a bundle across configuration changes! All of this together gives us a unit testable, encapsulated class of business logic that's easy to read and is decoupled from anything in the Android framework! Here's the simple yet elegant piece of code that makes the app work within the view. Nothing is better than a lightweight activity with little/no business logic. All it does it render what needs to be shown!
private fun renderState(weatherListViewState: WeatherListViewState) {
if(weatherListViewState.zipCode == null) {
if (weatherListViewState.initialState) determineAndShowStartupState()
else showZipCodeEntryDialog(
if (weatherListViewState.showErrorDialog)
R.string.error_finding_zip_code_prompt else
R.string.enter_zip_code_prompt)
}
setProgressSpinnerVisible(weatherListViewState.showingProgressSpinner)
setWeatherDataVisible(!weatherListViewState.showingProgressSpinner)
showZipCodeMenuItemEnabled(!weatherListViewState.showingProgressSpinner)
weatherListViewState.locationInfo?.let{ showToastWithLocationInfo(it) }
val controller = WeatherListEpoxyController()
forecast_list.adapter = controller.adapter
controller.setData(weatherListViewState.forecasts, weatherListViewState.currentWeather)
}
Dagger is a static dependency injection framework that allows for easily creating compositional software modules. Dagger allowed me to focus on the main classes of the project and then simply define how they are related. This alleviated the job of creating each object from within other objects, thus allowing me to simplify classes by removing factory-like instantiation methods. Dagger generates and handles the code for tieing everything together.
RxJava/RxAndroid allows us to use an event-driven reactive style of programming for handling our network interactions. This is extremely nice as it works just like the ViewModel itself works (it exposes a stream of events). With RxJava, we get the simplicity of handling asynchronous network calls with ease and responding to associated events within the stream. Take a look at the following piece of code from the ViewModel which is extremely easy to read/understand.
private fun getForecast(zipCode: Int){
weatherRepository.getForecasts(zipCode)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { forecastResultToForecastModelMapper.convertToListOfModels(it) }
.subscribe(::showForecasts, ::showErrorDialog)
}
It's easy to see what's happening. We get the data from the repository off the main thread, map it to the correct models on the main thread, then update the viewstate which will notify the view that a change has been made within its own stream of updates.
Epoxy is Airbnb's library which allows us to create a complex recyclerview. It uses its own MVC architecture where you define a controller to build in the recycler view, each of which handles their own rendering. This allows us to create a RecyclerView whose adapter has multiple types of viewholders. You'll notice that the first element of the weather list has a different layout from every other element (the forecast). We can do this with epoxy without having to write an extension to the RecyclerView.Adapter. Pretty slick!
- Why app architecture matters
- Android Architecture Components — now with 100% more MVVM
- Model–view–viewmodel
- Simple Android MVVM using RX and Kotlin
- Android architecture's with examples
If you have any questions about the repo/how to build your own MVVM architected app, feel free to reach out to me with any questions/comments/concerns!
- Epoxy - Epoxy is an Android library for building complex screens in a RecyclerView
- Dagger - A fast dependency injector for Android and Java.
- RxAndroid - RxJava bindings for Android
- RxKotlin - RxJava bindings for Kotlin
- Retrofit - Type-safe HTTP client for Android and Java by Square, Inc.
- Picasso - A powerful image downloading and caching library for Android
- Anko - Pleasant Android application development. Used for accessing thread schedulers
- Gson - A Java serialization/deserialization library to convert Java Objects into JSON and back
- Architecture Components - A collection of libraries that help you design robust, testable, and maintainable apps
- Imgur - Image hosting website, used to host images for this readme
- OpenWeatherMap API - Simple and fast and free weather API from OpenWeatherMap you have access to current weather data/forecast
- Zipcode api - Used to access zip code location information from user entered zip
- UESP.net - Used to download all icons from the oblivion repository and access information about tamriel
- Firebase - Used to house images for icons to load with picasso
- Photoshop - The world’s best imaging and design app .Used to create images for site. Used to create luancher icon and adjust size of weather icons.
- Android Studio - Android Studio provides the fastest tools for building apps on every type of Android device
- Github - GitHub is a web-based Git or version control repository and Internet hosting service
- EZGif - Online GIF maker and image editor. Used to create gifs from mp4's for this readme