从Fragment返回时,ViewModel onchange会被多次调用

Nik*_*zic 11 android android-livedata

我正在使用Android架构组件.我想要的是当用户在Edittext中键入"0"并单击按钮以将Fragment替换为新的,并且如果键入任何其他内容后发布Toast错误消息.在问题是当我从新的Fragment(BlankFragment)返回并再次单击按钮并再次输入"0"并单击时,onchange()被多次调用,因此片段被多次创建

FragmentExample.class:

     @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        manager = getActivity().getSupportFragmentManager();
        viewmModel = ViewModelProviders.of(getActivity(), viewModelFactory)
                .get(VModel.class);

        View v = inflater.inflate(R.layout.fragment_list, container, false);   
        b = (Button) v.findViewById(R.id.b);
        et = (EditText) v.findViewById(R.id.et);

        viewmModel.observeData().observe(getActivity(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {

                if(s.equals("0")) {
                    BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
                    if (fragment == null) {
                        fragment = BlankFragment.newInstance();
                    }
                    addFragmentToActivity(manager,
                            fragment,
                            R.id.root_activity_detail,
                            DETAIL_FRAG
                    );
                } else {
                    Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
                }
            }
        });

        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewmModel.setData(et.getText().toString());
            }
        });
        return v;
    }
    private void addFragmentToActivity(FragmentManager fragmentManager, BlankFragment fragment, int root_activity_detail, String detailFrag) {
        android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(root_activity_detail, fragment, detailFrag).addToBackStack(detailFrag);
        transaction.commit();
    }
Run Code Online (Sandbox Code Playgroud)

Reopistory类:


    public class Repository {
    MutableLiveData<String> dataLive = new MutableLiveData<>();  

    public Repository() {

    }

    public void setListData(String data) {
       dataLive.setValue(data);
    }

    public MutableLiveData<String> getData() {
        return dataLive;
    }
}
Run Code Online (Sandbox Code Playgroud)

}

BlankFragment.class:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        listItemViewModel = ViewModelProviders.of(this, viewModelFactory)
                .get(VModel.class);
        listItemViewModel.setData("");
        return inflater.inflate(R.layout.fragment_blank, container, false);
    }
Run Code Online (Sandbox Code Playgroud)

Bip*_*Das 15

这是我如何解决这个问题的一个例子。[测试和工作]

 viewModel.getLoginResponse().observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String response) {
            if(getViewLifecycleOwner().getLifecycle().getCurrentState()== Lifecycle.State.RESUMED){
                // your code here ...
            }

        }
    });
Run Code Online (Sandbox Code Playgroud)


Jul*_*Neo 10

这里的问题是,当您从活动中删除片段时,片段及其视图模型都不会被销毁.当你回来时,你会livedata在旧的观察者仍然在同一个片段中的时候添加一个新的观察者(如果你添加了观察者onCreateView()).有一篇文章(实际上甚至是一个SO线程)谈论它(使用解决方案).

修复它的简单方法(也在文章中)是在向其添加观察者之前从livingata中删除任何观察者.

更新:在支持lib v28中,一个名为ViewLifeCycleOwner的新LifeCycleOwner应修复此处的更多信息

  • 使用viewLifecycleOwner也有同样的问题。当 popBack 到使用发出的最后一个值调用的片段 liveData 时。您应该将观察者放在 onCreate 中并将其用作所有者。 (9认同)

Sam*_*net 9

你不应该创建你的viewmModelinonCreateView而是 in ,onCreate这样你就不会在每次创建视图时向你的数据添加一个监听器。

  • 如果使用 viewLifecycleOwner 你不能在 onCreate 方法上创建观察者,因为你不能在 onCreateView() 之前或 onDestroyView() 之后调用 viewLifecycleOwner (8认同)

小智 8

这是你做错了什么......

  viewmModel.observeData().observe(getActivity(), new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {

        if(s.equals("0")) {
            BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
            if (fragment == null) {
                fragment = BlankFragment.newInstance();
            }
            addFragmentToActivity(manager,
                    fragment,
                    R.id.root_activity_detail,
                    DETAIL_FRAG
            );
        } else {
            Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,您可以使用“this”或“viewLifecycleOwner”代替“getActivity()”。

因为当您在observe 方法中传递getActivity() 时,每当您打开您的片段时,您都将观察者的新实例与Activity 而非片段附加在一起。所以即使你杀死了你的片段,观察者也会保持活力。所以当livedata postvalue时,它会向所有的观察者发送数据,因为观察者太多了,那么所有的观察者都会得到通知。因此,您的观察者被调用了太多次。所以你必须像这样观察片段中的实时数据。

  viewmModel.observeData().observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {

        if(s.equals("0")) {
            BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
            if (fragment == null) {
                fragment = BlankFragment.newInstance();
            }
            addFragmentToActivity(manager,
                    fragment,
                    R.id.root_activity_detail,
                    DETAIL_FRAG
            );
        } else {
            Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
        }
    }
});  
Run Code Online (Sandbox Code Playgroud)

但是你的 onchanged 方法仍然会被调用两次。
您可以通过检查 onchanged 方法中的一个条件来停止此操作。

    dash_viewModel.getDashLiveData().observe(viewLifecycleOwner, object : Observer<AsyncResponse> {
        override fun onChanged(t: AsyncResponse?) {
            if(viewLifecycleOwner.lifecycle.currentState==Lifecycle.State.RESUMED){
                setData(t)
            }

        }

    })
Run Code Online (Sandbox Code Playgroud)

从我的研究中,我发现,如果使用其相应活动的 ViewModel 进行片段化,那么即使您开始观察实时数据,它也会首先向您发送最近发出的项目。即使你没有从你的片段中调用它。

所以 onChange 方法被调用了两次

  1. 当片段处于​​开始状态时 - 接收最近发出的项目

  2. 当片段处于​​恢复状态时 - 接收片段为 api 发出的调用。

所以改变了我总是像这样在 viewLifecycleOwner 的帮助下检查片段的状态

   if(viewLifecycleOwner.lifecycle.currentState==Lifecycle.State.RESUMED){
      // if the fragment in resumed state then only start observing data
        }
Run Code Online (Sandbox Code Playgroud)

viewlifecycleowner 由 Fragments 和 Activity 提供,因为 Google 直接在支持库 28.0.0 和 androidx 中使用 getViewLifecycleOwner() 方法实现了这个解决方案。viewlifecycleowner 包含有关组件生命周期的信息。

在 Java 中,您可以使用 getViewLifecycleOwner() 而不是 viewlifecycleowner 。


Che*_*eng 7

而不是使用getActivityLifecycleOwner,您应该使用片段。

更改

viewmModel.observeData().observe(getActivity(), new Observer<String>() {
Run Code Online (Sandbox Code Playgroud)

viewmModel.observeData().removeObservers(this);
viewmModel.observeData().observe(this, new Observer<String>() {
Run Code Online (Sandbox Code Playgroud)