View Model продолжает создавать экземпляр Live Data - PullRequest
0 голосов
/ 15 декабря 2018

Я создал экземпляр модели представления в onCreate методе действия.

    ticketViewModel = ViewModelProviders.of(this).get(TicketViewModel.class);

Затем у меня есть метод AddTicket, который использует viewModel для обращения к службе и при ответес viewModel я отклоняю загрузку анимации.

 public void addTicket(View view){

     ticketViewModel.AddTicket(id).observe(this, response ->{
                        dismissLoadingAnimation();
    } 

Теперь после добавления заявки пользователь может нажать кнопку Add Ticket, и метод addTicket() будет вызван снова.

но на этот раз observer, определенный во ViewModel, вызывается 2 раза, что приводит к 2 сетевым вызовам и 2 dismissLoadingAnimation выполнению.

И если я продолжаю нажимать кнопку addTicket, то количество выполняемых наблюдателей определено внутриViewModel продолжайте увеличиваться.

Это мой код модели View.

public class TicketViewModel extends AndroidViewModel implements IServiceResponse {

    MutableLiveData<String> mObservableResponse = new MutableLiveData<String>();


    public MutableLiveData AddTicket(String id){

        JsonObject jsonObject= new JsonObject();
        jsonObject.addProperty("id",  id);

        NetworkUtility networkUtility= new NetworkUtility(this, ADD_TICKET);
        networkUtility.hitService(URL, jsonObject, RequestMethods.POST);

        return mObservableResponse;
    }


     @Override
        public void onServiceResponse(String response, String callType){

        if(serviceTag.equalsIgnoreCase(ADD_TICKET)){    
             mObservableResponse.setValue("success");
        }
    }

}

Ответы [ 5 ]

0 голосов
/ 25 декабря 2018
  • Цель Viewmodel состоит в том, чтобы представить наблюдаемые (Livedata)
  • Цель View (Activity / Fragment) - получить эти наблюдаемые и наблюдать их
  • Всякий раз, когда естьИзменение в этих наблюдаемых (Livedata) изменения автоматически публикуется для активных подписанных владельцев (Activity / Fragment), поэтому вам не нужно удалять их в onPause / onStop, так как это не обязательно

Я могу предложитьНесколько изменений в вашем коде для решения проблемы с указанными выше указателями

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");
        }
    }

}

Просмотр

    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);
    }

Надеюсь, это поможет:)

0 голосов
/ 24 декабря 2018

Если вы хотите внести некоторые изменения, я думаю, что мы сможем справиться с этим более чётким способом

LiveData предназначен для использования в качестве значения свойства представления


В 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);
        }
    }

}

В onCreate вашей деятельности / фрагмента

ticketViewModel.getShowLoadingAnimationLiveData().observe(this,showLoadingAnimation->{
    if(showLoadingAnimation != null && showLoadingAnimation){
        startLoadingAnimation();
    }else{
        dismissLoadingAnimation();
    }
})

Основная концепция заключается в разделении обязанностей, Деятельности / фрагменту не нужно знать, какой процесс происходит, им нужно только знать, каковы текущие свойства / состояние дочерних представлений.,

Нам необходимо поддерживать LiveData в ViewModels для каждого изменяющегося свойства / состояния в зависимости от представлений.ViewModel должен обрабатывать состояния представления в зависимости от того, что происходит.

Единственная ответственность, которую Activity / Fragment несет в отношении процесса, - это запускать его и забывать, а ViewModel должен обрабатывать все (например, информирование репозиториев о выполнении работы и изменение View.Свойства).

В вашем случае 'addTicket' - это процесс, о котором Деятельности / Фрагменту не нужно знать о своем статусе.Единственная ответственность Activity / Fragment об этом процессе - запускать его.

ViewModel - это тот, кому необходимо проанализировать состояние процесса (выполняется / успешно / не удалось) и дать соответствующие значения LiveDatas для информирования.соответствующие виды

0 голосов
/ 24 декабря 2018

Количество выполняемых наблюдателей, определенное внутри ViewModel, увеличивается с каждым кликом. Вы регистрируете новых наблюдателей.Вы не должны регистрировать наблюдателя с помощью метода onClick().

Вы должны сделать это методом onCreate() вашего Activity или методом onViewCreated вашего фрагмента.Если Ты это сделаешь, тебе не понадобится removeObserver, когда Ты закончишь работу.Lifecycle механизм покроет это для вас.

Но если вы действительно хотите получить ответ на свой вопрос, вот как вы можете это сделать

yourViewModel.yourList.removeObservers(this)

Передача this означает пропуск вашего Activity, или есть второй способ:

yourViewModel.yourList.removeObserver(observer)

val observer = object : Observer<YourObject> {
    override fun onChanged(t: YourObject?) {
        //todo
    }
}
0 голосов
/ 24 декабря 2018

У меня была похожая проблема.Вы можете попробовать использовать SingleLiveEvent

Или, в моем более сложном случае, мне пришлось использовать пользовательский наблюдатель.Это выглядело бы так:

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();
    } 
}

И используя его как:

public void addTicket(View view){

     ticketViewModel.AddTicket(id).observe(this, myCustomObserver);
}
0 голосов
/ 23 декабря 2018

Вам нужно вызвать observe только один раз, я предпочитаю сделать это в onResume, а затем вызвать removeObserver в onPause:

Добавляет данного наблюдателя к наблюдателямlist

Вы продолжаете добавлять слушателей к данным, чтобы получить несколько обратных вызовов.

Редактировать:
Я взял мой существующий пример кода дляFragment и переименовал все (надеюсь), здесь нет примера для установки данных в ViewModel, но в вашем случае это должно быть ticketViewModel.AddTicket(id);.

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;
        }
    }

}
...