ViewModel & LiveData (Java)
ViewModel and LiveData are part of Android Jetpack’s Architecture Components. They solve two major problems:
- Retain UI state across configuration changes (e.g., rotation).
- Observe data changes in a lifecycle-aware way (no memory leaks, no manual unregister).
Big Picture
Repository
Fetches data (network/db)
ViewModel
Holds state, exposes LiveData
LiveData
Notifies observers (Activity/Fragment)
UI
Updates automatically
ViewModel Basics
// CounterViewModel.java
public class CounterViewModel extends ViewModel {
private final MutableLiveData<Integer> counter = new MutableLiveData<>(0);
public LiveData<Integer> getCounter() { return counter; }
public void increment() {
counter.setValue(counter.getValue() + 1);
}
}
Explanation:
- ViewModel survives rotation. When Activity recreates, the same instance is reused.
- LiveData is observable and lifecycle-aware.
Using in Activity
// MainActivity.java
public class MainActivity extends AppCompatActivity {
private CounterViewModel vm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tvCount);
Button btn = findViewById(R.id.btnInc);
vm = new ViewModelProvider(this).get(CounterViewModel.class);
vm.getCounter().observe(this, value -> tv.setText(String.valueOf(value)));
btn.setOnClickListener(v -> vm.increment());
}
}
Explanation:
- observe(this, ...) attaches observer tied to Activity lifecycle.
- UI auto-updates when setValue() is called in ViewModel.
- Even after rotation, counter value persists.
LiveData Types
| Type | Description | Usage |
|---|---|---|
| LiveData<T> | Read-only observable | Expose from ViewModel |
| MutableLiveData<T> | Mutable subclass | Update inside ViewModel |
| MediatorLiveData<T> | Combine multiple sources | Merge DB + network updates |
| Transformations.map | Derive data | e.g., format strings |
| Transformations.switchMap | Switch source | e.g., new query triggers new LiveData |
Shared ViewModel Between Fragments
// In Activity hosting fragments
SharedVM vm = new ViewModelProvider(this).get(SharedVM.class);
// In Fragment A
vm.getSelected().observe(getViewLifecycleOwner(), item -> { /* update */ });
vm.select("User42");
// In Fragment B
vm.getSelected().observe(getViewLifecycleOwner(), item -> { /* receive update */ });
Explanation:
requireActivity() scopes ViewModel to Activity so all its fragments share the same instance.
Lifecycle Awareness
Activity/Fragment lifecycle
├─ onStart() → LiveData delivers updates
├─ onResume() → continues observing
└─ onStop() → observers automatically paused
Mini Charts
Troubleshooting
UI not updating? → Ensure you call setValue() on main thread or postValue() on background thread.
Value lost after rotation? → Check you are using ViewModel, not just LiveData in Activity.
Multiple observers firing? → Verify you scope observers to getViewLifecycleOwner(), not getActivity().
Hands-On Practice
- Create a TimerViewModel that counts seconds with Handler.
- Observe the time in Activity and display it in a TextView.
- Rotate the screen – the timer should continue from last value.
- Add another Fragment observing the same timer via shared ViewModel.
Repository (API/DB)
└─ ViewModel (holds LiveData)
└─ LiveData (exposed)
├─ Activity observes
└─ Fragment observes
→ Both update automatically