Manual DI is for sure a viable option for Android dev, but for simplicity, I'd recommend using the Koin framework to handle dependency injection.
This class is doing way too much and will continue to grow in size during the project's lifetime. It's best to extract out responsibilities and move them to dedicated classes.
The external class would also prevent needing to access prefs this way each place it is needed
I suggest placing the shared prefs behind an interface that manages a UserSettings or UserPreferecens of some sort. Internally shared preferences can be used. This promotes decoupling impl details with intention/domain logic.
With Kotlin Coroutines, you can do context switching easier using withContext()
which can help prevent the ANR issue. You can make classes and functions "main-safe", meaning it won't block the main thread when called. This means you can call loadDataFromDatabase()
without dropping frames.
I suggest only exposing a single function in your ViewModel for consistency. Each screen would then have their actions to be taken modelled in a sealed interface
and the ViewModel will process it accordingly to update the state. It is possible to pass in parameters to these Action
class types
I highly recommend moving to Compose, especially since this is a greenfield project. Compose interops nicely with the View system, so If you need to use libraries that don't support Compose, then you can wrap it in an AndroidView
composable. The declarative style of Compose also aids in the readability of the project.
Conventionally, there is a Compose Screen (or Fragment, but I since this is a new project - I recommend using Compose) per screen. Each screen has its own ViewModel. A MainViewModel has its place, but its not suppose to handle screen-specific logic.
What is the purpose of the 2nd activity? by the name of it, it could be changed into a fragment or compose screen. A 2nd activity isn't bad, and there are certainly some usecases for it, but I'm not sure this is one.
The READ_EXTERNAL_STORAGE
and WRITE_EXTERNAL_STORAGE
have been replaced by Android's scoped storage (since Android 10 - API 29), and these permissions will be ignored on newer Android devices. WRITE_EXTERNAL_STORAGE
will be ignored on Android 11+ (API 30+). For READ_EXTERNAL_STORAGE
, there is the more granular variants:
Prefer to work with Kotlin's StateFlow
, as it is much more flexible with its many intermediary operators. It can also be observed in a lifecycle manner - so there LiveData
loses its edge it originally had 😂