【问题标题】:View Model keeps creating instance of Live DataView Model 不断创建 Live Data 的实例
【发布时间】:2018-12-15 20:44:04
【问题描述】:

我在活动的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()方法将被再次调用。

但是这次在 ViewModel 中定义的 observer 被调用了 2 次,导致 2 次网络调用和 2 次 dismissLoadingAnimation 执行。

如果我一直按addTicket 按钮,ViewModel keep 中定义的执行观察者的数量会增加。

这是我的视图模型代码。

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

}

【问题讨论】:

  • 我想你的意思不是要把“sectionViewModel.getSeats”放在“sectionViewMode.getData”里面,对吗?
  • @AhmedAshrafGamal : 首先我调用sectionViewModel.getData,当我从这个方法得到结果时,我需要调用sectionViewModel.getSeats
  • 尝试记录而不是调试 Log.i("test","test");在线程内调试有时会给出错误的结果。
  • 你确定你的服务没有调用 onResponse 两次吗?你能证明一下吗?

标签: android performance android-layout android-viewmodel


【解决方案1】:

ViewModelkeep 中定义的执行观察者的数量会随着每次点击而增加,因为您正在注册新的观察者。您不应该使用onClick() 方法注册观察者。

您应该在 ActivityonCreate() 方法或片段的 onViewCreated 方法中执行此操作。如果你这样做,那么当你完成工作时就不需要removeObserverLifecycle 机制将为您覆盖它。

但如果你真的想回答你的问题,你可以这样做

yourViewModel.yourList.removeObservers(this)

传递this意味着传递你的Activity,或者还有第二种方式:

yourViewModel.yourList.removeObserver(observer)

val observer = object : Observer<YourObject> {
    override fun onChanged(t: YourObject?) {
        //todo
    }
}

【讨论】:

    【解决方案2】:
    • Viewmodel 的目的是公开 observables (Livedata)
    • View(Activity/Fragment) 的目的是获取这些 observables 并观察它们
    • 只要这些 observables (Livedata) 发生更改,更改就会自动发布给活动的订阅所有者 (Activity/Fragment),因此您无需在 onPause/onStop 中删除它们,因为这不是强制性的

    我可以建议对您的代码进行一些更改以解决上述问题的问题

    视图模型

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

    希望对你有帮助:)

    【讨论】:

      【解决方案3】:

      你只需调用observe一次,我更喜欢在onResume中调用,然后在onPause中调用removeObserver

      将给定的观察者添加到观察者列表中

      您不断向数据添加侦听器,以便获得多个回调。

      编辑:
      我为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;
              }
          }
      
      }
      

      【讨论】:

      • 你能告诉我如何调用removeObserver方法的代码吗?
      【解决方案4】:

      我有类似的问题。您可以尝试使用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);
      }
      

      【讨论】:

        【解决方案5】:

        如果你愿意做一些改变,我想我们可以用更干净的方式来处理它

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

        在 Activity/Fragment 的“onCreate”中

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

        主要概念是分工, Activity/Fragment 不需要知道哪个进程正在进行,他们只需要知道子视图的当前属性/状态是什么。

        我们需要在 ViewModels 中为每个变化的属性/状态维护一个 LiveData,具体取决于 Views。 ViewModel 需要根据发生的情况来处理视图状态。

        Activity/Fragment 对进程的唯一责任是触发它并忘记它,而 ViewModel 需要处理所有事情(例如通知存储库完成工作和更改视图属性)。

        就你而言, 'addTicket' 是一个进程,Activity/Fragment 不需要知道那里的状态。 Activity/Fragment 关于该进程的唯一职责就是触发它。

        ViewModel 需要分析进程的状态(进行中/成功/失败)并为 LiveDatas 提供适当的值以通知各个视图

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-09
          • 2012-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多