I created the instance of View Model in onCreate
method of an activity.
ticketViewModel = ViewModelProviders.of(this).get(TicketViewModel.cl
The number of executing observer defined inside ViewModel
keep increases becasue with every click You're registering new observers. You're not supposed to register observer with onClick()
method.
You should do it in onCreate()
method of your Activity
or in onViewCreated
method of your fragment. If You'll do that, there won't be a need to removeObserver
when You'll finish work. Lifecycle
mechanism will cover it for you.
But if you really want answer for you question, this is how you can do it
yourViewModel.yourList.removeObservers(this)
Passing this
means passing your Activity
, or there is a second way:
yourViewModel.yourList.removeObserver(observer)
val observer = object : Observer<YourObject> {
override fun onChanged(t: YourObject?) {
//todo
}
}
I had similar problem. You could try to use SingleLiveEvent
Or, in my, more complicated case, i had to use custom observer. It would looks like this:
public class CustomObserver implements Observer<YourType> {
private MyViewModel mViewModel;
public CustomObserver (){}
public void setViewModel(MyViewModel model) {
mViewModel = model;
}
@Override
public void onChanged(@Nullable YourType object) {
mViewModel.AddTicket(id).removeObserver(this); // removing previous
mmViewModel.refreshTickets(); // refreshing Data/UI
// ... do the job here
// in your case it`s: dismissLoadingAnimation();
}
}
And using it like:
public void addTicket(View view){
ticketViewModel.AddTicket(id).observe(this, myCustomObserver);
}
You only need to call the observe
once, I prefer to do it in onResume
and then call removeObserver
in onPause
:
Adds the given observer to the observers list
You keep adding listeners to the data so you get multiple callbacks.
Edit:
I took an existing code sample of mine for a Fragment
and renamed everything (I hope), there's no example here for setting the data into the ViewModel
but it should be ticketViewModel.AddTicket(id);
in your case.
public class ListFragment extends Fragment {
private MyViewModel viewModel;
private MyRecyclerViewAdapter recyclerViewAdapter;
private Observer<List<DatabaseObject>> dataObserver;
private RecyclerView recyclerView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_layout, container, false);
initRecyclerView(rootView, getContext());
initObservers();
return rootView;
}
private void initRecyclerView(View rootView, Context context) {
recyclerViewAdapter = new MyRecyclerViewAdapter(context);
recyclerView = rootView.findViewById(R.id.recycler_view);
recyclerView.setAdapter(recyclerViewAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(new DividerNoLastItemDecoration());
}
private void initObservers() {
dataObserver = new Observer<List<DatabaseObject>>() {
@Override
public void onChanged(@Nullable final List<DatabaseObject> data) {
recyclerViewAdapter.setData(data);
}
};
}
@Override
public void onResume() {
super.onResume();
initViewModel();
}
private void initViewModel() {
FragmentActivity activity = getActivity();
if (activity != null) {
viewModel = ViewModelProviders.of(activity).get(MyViewModel.class);
viewModel.getData().observe(activity, dataObserver);
}
}
@Override
public void onPause() {
super.onPause();
if (viewModel != null) {
viewModel.getData().removeObserver(dataObserver);
viewModel = null;
}
}
}
If you are willing to do some changes, i think we can handle it in much cleaner way
LiveData is meant to be used to contain a property value of a view
In ViewModel
public class TicketViewModel extends AndroidViewModel implements IServiceResponse {
private MutableLiveData<Boolean> showLoadingAnimationLiveData = new MutableLiveData<String>();
public LiveData<Boolean> getShowLoadingAnimationLiveData(){
return showLoadingAnimationLiveData;
}
public void addTicket(String id){
JsonObject jsonObject= new JsonObject();
jsonObject.addProperty("id", id);
NetworkUtility networkUtility= new NetworkUtility(this, ADD_TICKET);
networkUtility.hitService(URL, jsonObject, RequestMethods.POST);
showLoadingAnimationLiveData.setValue(true);
}
@Override
public void onServiceResponse(String response, String callType){
if(serviceTag.equalsIgnoreCase(ADD_TICKET)){
showLoadingAnimationLiveData.setValue(false);
}
}
}
In 'onCreate' of your Activity/Fragment
ticketViewModel.getShowLoadingAnimationLiveData().observe(this,showLoadingAnimation->{
if(showLoadingAnimation != null && showLoadingAnimation){
startLoadingAnimation();
}else{
dismissLoadingAnimation();
}
})
The main concept is to divide the responsibilities, Activity/Fragment doesn't need to know which process is going on, they only need to know what are the current properties/state of there child views.
We need to maintain a LiveData in ViewModels for each changing property/state depending on Views. ViewModel needs to handle the view states depending on whats happening.
Only responsibility the Activity/Fragment has about a process is to trigger it and forget and ViewModel needs handle everything(like informing Repositories to do the work and changing View Properties).
In your Case, 'addTicket' is a process about which Activity/Fragment doesn't need to know about there status. The only responsibility of Activity/Fragment about that process is to trigger it.
ViewModel is one who needs to analyze the state of process(in-progress/success/failed) and give appropriate values to the LiveDatas to inform the respective Views
I can suggest few changes to your code to solve the problem with the above mentioned pointers
ViewModel
public class TicketViewModel extends AndroidViewModel implements IServiceResponse {
MutableLiveData<String> mObservableResponse = new MutableLiveData<String>();
public LiveData<String> getResponseLiveData(){
return mObservableResponse;
}
public void AddTicket(String id){
JsonObject jsonObject= new JsonObject();
jsonObject.addProperty("id", id);
NetworkUtility networkUtility= new NetworkUtility(this, ADD_TICKET);
networkUtility.hitService(URL, jsonObject, RequestMethods.POST);
}
@Override
public void onServiceResponse(String response, String callType){
if(serviceTag.equalsIgnoreCase(ADD_TICKET)){
mObservableResponse.setValue("success");
}
}
}
View
onCreate(){
ticketViewModel = ViewModelProviders.of(this).get(TicketViewModel.class);
observeForResponse();
}
private void observeForResponse(){
ticketViewModel.getResponseLiveData().observe(this, response ->{
//do what has to be updated in UI
}
}
public void addTicket(View view){
ticketViewModel.AddTicket(id);
}
Hope this is of help :)