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

场景:我有两个名为FirstFragment和 的片段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,回来FirstFragmentonViewCreated会再次调用和API LiveDataObserver将被触发两次!

我知道 onViewCreated 会再次调用,因为导航组件会替换片段而不是添加它们。但我不知道为什么 LiveData 观察者会被触发两次。

我读了这篇文章,但他似乎忽略了导航组件。

我需要一个解决方案...

  1. 避免onViewCreated再次调用代码。
  2. 避免再次触发 LiveData 观察者。

回答

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

我需要一个解决方案...

避免再次调用 onViewCreated 代码。

避免再次触发 LiveData 观察者。

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

我知道 onViewCreated 会再次调用,因为导航组件会替换片段而不是添加它们。

如您所知,在将片段添加到后台堆栈时替换它们,只需将旧片段从fragmentManager. 这意味着旧片段的视图将被破坏。

当您从后台堆栈中弹出UnitFragment时,它将创建它的视图。

所以不要调用任何 API 调用,onViewCreated因为它可能会调用多次(在配置更改、销毁片段等...)

最好onCreate用于初始化非视图相关的组件(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());
        });
    }
}


以上是使用导航组件多次触发LiveData观察者的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>