After reading Headfirst design patterns I want to know the simplest way to implement an observer design pattern in my app.
The context:
In the MainActivity of the app, after users input in EditTextView and click the button, an URL will be generated and sent to other activities for displaying. (also switches to other activity 1)
I want to make my MainACtivity as the Subject(Observable) and Activity1 and Activity 2 as my Observers.
Instead of using the built-in observer interface, I tried to use self-defined Observer and subject interfaces to implement this pattern(like how they did it in the book).
But this way includes creating a Subject instance in the Observer class, which means I will create a MainActivity instance in my other Observer classes, I am not sure if it works, could anyone tell me how to do it properly? (Or I just couldn't use an activity as a subject?)
CodePudding user response:
You cannot observe objects in different activities. All you can do is pass the data between activities using Intents.
In Android, the observer pattern is implemented by using the class ViewModel and the class LiveData / StateFlow. If you want to have 3 different screens that observe a single object for changes. You need to have 3 Fragments that share the same Activity and the same ViewModel.
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
The LiveData is here your Subject that you update indirectly through the selected variable. The selected variable can be changed by calling select() function from a Fragment. In each Fragment, you have to create the ViewModel and observe the LiveData.
public class ListFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
You can read more about the example above here: https://developer.android.com/topic/libraries/architecture/viewmodel#java
This solution is also preferable because the ViewModel survives the configuration changes and can restore the state of your activity even after the activity is destroyed.
Moreover, you shouldn't keep any references of one activity in another activity or in a ViewModel, as it can lead to memory leaks.
You can try to create your own observer pattern between objects in the same activity or ViewModel if you find a use-case for it, but in most cases LiveData and StateFlow should be enough for updating your UI.
CodePudding user response:
Any specific Activity instance is an ephemeral view holder. I say ephemeral because Android destroys and recreates it for any configuration change like a screen rotation. This is why it is not trivial to pass references between different Activities. It also causes memory leaks to pass around Activity instances. It is not viable to use an Activity as an observable.
What you can do is have your MainActivity publish changes to some repository class, and have that repository class expose observables that other Activities can view. In such a case, the repository must have application scope. It can be lazily created in your Application class and retrieved from the Application. If you're using a dependency injection framework, the repository would be an application-scoped singleton.
The common pattern for an Activity or Fragment to interact with a repository is through a ViewModel class.