java - 使用导航组件多次触发 LiveData 观察者

标签 java android android-livedata android-architecture-navigation android-mvvm

场景:我有两个名为 FirstFragment 的 fragment 和 UnitFragment .我来自 FirstFragmentUnitFragment选择一个单位返回FirstFragmet使用 navController.popBackStack();并将单位数据发送至FirstFragment这是观察单位数据。
这是我的onViewCreatedFirstFragment :

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    if (viewModel == null) { // Lazy Initialization
        ApiService apiService = ApiServiceProvider.getInstance();
        AddNewWareViewModelFactory addNewWareViewModelFactory = new AddNewWareViewModelFactory(apiService);
        viewModel = new ViewModelProvider(this, addNewWareViewModelFactory).get(AddWareViewModel.class);
    }

    Log.i(TAG, "OnViewCreated -----> Called");
    viewModel.callNewWare(parentCode);
    viewModel.getNewWareResponse().observe(getViewLifecycleOwner(),
            resObject -> Log.i(TAG, "API Response LiveData Count -----> " + count++)); // Started From Zero

    NavHostFragment navHostFragment = (NavHostFragment) requireActivity()
            .getSupportFragmentManager()
            .findFragmentById(R.id.container);

    binding.button.setOnClickListener(v -> {
        if (navHostFragment != null) {
            NavController navController = navHostFragment.getNavController();
            navController.navigate(FirstFragmentDirections.actionFirstFragmentToUnitFragment());
        }
    });


    if (navHostFragment != null) {
        NavController navController = navHostFragment.getNavController();
        NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
        if (navBackStackEntry != null) {
            SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
            MutableLiveData<Unit> unitLiveData = savedStateHandle.getLiveData("unit_data");
            unitLiveData.observe(getViewLifecycleOwner(), unit -> binding.tvUnit.setText(unit.getTitle()));
        }
    }
}
这是 LogCat 结果:
--- Go to FirstFragment for first time ---
I/FirstFragment: OnViewCreated -----> Called
I/FirstFragment: API Response LiveData Count -----> 0
--- Button clicked to go to UnitFragment to select a unit ---
I/UnitFragment: Selected Unit -----> Meter
--- Come back to FirstFragment ---
I/FirstFragment: OnViewCreated -----> Called
I/FirstFragment: API Response LiveData Count -----> 1
I/FirstFragment: API Response LiveData Count -----> 2
正如您在 LogCat 结果中看到的,每次我单击按钮并转到 UnitFragment并返回 FirstFragment onViewCreated将再次调用,API LiveDataObserver 将被触发两次!!!
我知道 onViewCreated 会再次调用,因为导航组件替换了 fragment 而不是添加它们。但我不知道为什么 LiveData 观察者会被触发两次。
我读了this post但他似乎忽略了导航组件。
我需要一个解决方案...
  • 避免调用 onViewCreated再次编码。
  • 避免再次触发 LiveData 观察者。
  • 最佳答案

    不幸的是,这不是您问题的答案:

    I need a solution to ...

    Avoid calling onViewCreated codes again.

    Avoid triggering LiveData observer again.


    我试图解释导航及其行为或纠正一些误解。这些问题有不同的原因和Avoid calling onViewCreated codes again.是一种迂回的方式。

    I know onViewCreated will call again because Navigation Component replaces the fragments instead of adding them.


    如您所知,将 fragment 添加到后台堆栈时进行替换,只需将旧 fragment 从 fragmentManager 中分离出来.表示旧 fragment 的查看 会破坏。
    当您从后台堆栈中弹出 UnitFragment 时,将创建其 View 。
    所以不要在 onViewCreated 中调用任何 API。因为它可能会调用多次(在配置更改、破坏 fragment 等中)
    最好使用onCreate用于初始化非 View 相关的组件(ViewModel + API 调用)。它减少了 Lazy(!?) Initialization查看。
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        ApiService apiService = ApiServiceProvider.getInstance();
        AddNewWareViewModelFactory addNewWareViewModelFactory = new AddNewWareViewModelFactory(apiService);
        viewModel = new ViewModelProvider(this /*owner*/, addNewWareViewModelFactory).get(AddWareViewModel.class);
    
        viewModel.callNewWare(parentCode);
    }
    
    
    并开始在 onViewCreated 中观察它们.此外,您应该使用 unit_data当你得到它时,来自 navBackStackEntry。
    if (navHostFragment != null) {
        NavController navController = navHostFragment.getNavController();
        NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
        if (navBackStackEntry != null) {
            SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
            MutableLiveData<Unit> unitLiveData = savedStateHandle.getLiveData("unit_data");
            unitLiveData.observe(getViewLifecycleOwner(), unit -> {
                savedStateHandle.remove("unit_data");       // add this line
                return binding.tvUnit.setText(unit.getTitle());
            });
        }
    }
    
    

    关于java - 使用导航组件多次触发 LiveData 观察者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67581800/

    相关文章:

    java - 无法将值放入 M​​DC

    java - 禁用checkstye.xml中缺少的javadoc注释

    android - WeakReference的get方法返回的强引用会导致AsyncTask onProgressUpdate内存泄漏吗?

    viewmodel - Livedata与Rxjava

    java - Helper 类中的计算问题 - 在计算之前返回值

    向量化计算的 Java 最佳实践

    java - 由于未找到 View 错误, ScrollView 被隐藏

    android - 在字符串中查找电话号码

    java - DiffUtil.ItemCallback 有时会丢失 oldItem 并导致 areContentsTheSame() 错误地返回 true

    java - 从 java 程序模拟移动 Web 浏览器