This will be the fifth installment in our series “Mastering Android ViewModels” where we dive deep into the essential dos and don’ts that can elevate your Android development skills. We’ve already covered several tips to improve performance and code quality in ViewModels, which have become an integral part of modern Android applications.
We’ve Covered So Far 🔄🔄🔄
- Avoid initializing the state in the
init {} block. ✅ Read here
- Avoid exposing mutable states. ✅ Read here
- Use
update{} when using
MutableStateFlows. ✅ Read here
- Try not to import Android dependencies in the ViewModels. ✅ Read here
- Lazily inject dependencies in the constructor. ✅ Read here
- Embrace more reactive and less imperative coding. ✅ Read here
- Avoid initializing the ViewModel from the outside world. ✅ Read here
In this article we’ll cover:
8. 👉Avoid hardcoding Coroutine Dispatchers.
9. 👉Unit test your ViewModels.
10. 👉Avoid exposing suspended functions.
#8 — Avoid Hardcoding Coroutine Dispatchers
When dealing with coroutines in your ViewModel, hardcoding dispatchers like Dispatchers.IO
or Dispatchers.Default
might seem convenient, but it can lead to tightly coupled and less testable code.
The Problem with Hardcoding Dispatchers
Hardcoding dispatchers directly in your ViewModel can make testing difficult and reduce flexibility. For instance, during testing, you may want to control the threading behavior, which becomes challenging with hardcoded dispatchers.
Recommended Approach
Inject your dispatchers via the constructor or use a dependency injection framework like Hilt or Dagger. This not only makes your ViewModel more flexible but also simplifies testing:
class MyViewModel(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : ViewModel() {
private fun loadData() {
viewModelScope.launch(ioDispatcher) {
// Your coroutine code here
}
}
}
By using dependency injection, you can swap out the dispatcher during testing, ensuring your ViewModel behaves correctly in different environments.
for an example look at:
Job Offers
#9 — Unit Test Your ViewModels
Unit testing is essential to ensure your ViewModels behave as expected. Without proper tests, you risk introducing bugs that could have been caught early.
Testing Challenges
ViewModels often interact with complex state and other components, making them tricky to test. However, by following the right practices, specially what we discuss in this series, you can thoroughly test your ViewModel’s logic.
Best Practices for Testing ViewModels
- Use a
TestCoroutineDispatcher to control coroutine execution and test asynchronous code synchronously.
- Favor testing ViewModels as a non-Android test (use test folder instead of androidTest)
- Avoid using
runBlocking{}
for testingsuspended
functions, instead userunTest{}
fromcoroutines-test
- Avoid manually peeking values from
StateFlows
, Use Turbine instead - For testing
flows
, use Turbine - Favor fakes over mocks
#10 — Avoid Exposing Suspended Functions
While suspend
functions make asynchronous programming in Kotlin easier, exposing them directly from your ViewModel can lead to misuse and increased complexity.
Why It’s Problematic
Exposing suspend
functions can result in mismanagement of threading or lifecycle events, leading to bugs or crashes.
The Better Way
Keep suspension internal to the ViewModel, and expose results through Flow
or other observable patterns.
Conclusion:
Mastering ViewModels in Android development is crucial for creating robust, efficient, and maintainable applications. Throughout this series, we’ve discussed a comprehensive set of best practices to improve your code quality and application performance.
🌟 Congratulations if you’ve made it this far in the article! 🎉 Don’t forget to:
- 👏 smash the clap button as many times! So I can continue with the follow-up articles!
- Follow my YouTube channel for video tutorials and tips on Android development
- ✨✨ If you need help with your Android ViewModels, Project, or your career development, Book a 1:1 or a Pair-Programming session with me, Book a time now 🧑💻🧑💻🧑💻
- check out the previous articles in this series with the links below:
This article is previously published on proandroiddev.com