Android MVVM架构中点击监听器与SharedPreference的放置疑问
Hey there! Let's tackle these two common MVVM confusion points—they're super common when you're getting started with the pattern, so great questions to ask!
1. Where to Place Click Listeners (Navigation vs. Business Logic)
The key here is remembering MVVM's core separation: ViewModel handles business logic, View handles UI-specific actions and interactions.
Navigation actions (starting an Activity/Fragment)
These belong firmly in the View layer. ViewModels should stay decoupled from Android framework components likeContextorActivity—this keeps them testable and reusable across different parts of your app. You can either:- Handle the click directly in your Activity/Fragment (e.g., call
startActivity()when a button is tapped), or - Use an event stream (like
LiveDataorFlow) in the ViewModel to signal that navigation is needed. The View observes this event and executes the actual jump.
Example: ViewModel exposes a
navigateToHomeEventLiveData. Your Fragment observes it, and when triggered, launches the Home Activity.- Handle the click directly in your Activity/Fragment (e.g., call
Non-navigation business logic
Any logic that doesn't involve UI navigation (like calculating values, triggering network calls, or updating local data) belongs in the ViewModel. The View's job is just to trigger the action (e.g., callviewModel.processFormData()on button click), then observe ViewModel state updates to refresh the UI.
2. SharedPreferences in the Model Layer: Standalone Manager vs. Integrated into Business Models
Both approaches work, but a standalone SharedPreferences manager is the cleaner, more scalable choice thanks to the single responsibility principle.
Standalone SharedPreferences Manager
Create a dedicated class (e.g.,AppSharedPrefs) that wraps all SharedPreferences operations—save, read, delete, clear. This class handles nothing but storage logic, then your business Model classes (likeLoginModel) depend on it to persist/retrieve data.
Benefits:- Reusability: Multiple Model classes can use the same manager instead of duplicating SP code.
- Testability: You can easily mock the manager to test your Model's business logic without relying on real SharedPreferences.
- Flexibility: If you ever want to switch to Jetpack DataStore or another storage solution, you only need to update this one class, not every Model that uses storage.
Here's a quick code example:
// Standalone SP manager class AppSharedPrefs(private val context: Context) { private val prefs = context.getSharedPreferences("app_storage", Context.MODE_PRIVATE) fun saveUserCredentials(username: String, password: String) { prefs.edit() .putString("saved_username", username) .putString("saved_password", password) .apply() } fun getSavedUsername(): String? = prefs.getString("saved_username", null) } // Business Model class class LoginModel(private val sharedPrefs: AppSharedPrefs) { fun onLoginSuccess(username: String, password: String) { // Handle login-related business logic (e.g., log events) // Delegate storage to the manager sharedPrefs.saveUserCredentials(username, password) } }Integrated into Business Models
You can embed SP logic directly into a business Model (like putting save/load code right inLoginModel), but this mixes business logic with storage logic. It works for small, isolated use cases, but becomes messy as your app grows—you'll end up duplicating code and making testing harder.
内容的提问来源于stack exchange,提问作者Muhamed Raafat

