在Android开发中,ViewModel 是一个用于在 ActivityFragment 中共享数据的组件。对于父 Fragment 和其内部的子Fragment(通过 ChildFragmentManager 加载的Fragment),是否能共用一个 ViewModel,是许多开发者关心的问题。


一、答案:父Fragment和子Fragment是否共用ViewModel?

默认情况下,父Fragment和子Fragment不会共用同一个ViewModel。

原因分析

  1. ViewModel的作用域(Scope)
  • ViewModel 的生命周期与其作用域(Scope) 一致,通常以 ActivityFragment 作为作用域。
  • 如果在 父Fragment子Fragment 中分别调用 ViewModelProvider(this),那么 父Fragment子Fragment 将各自创建自己的 ViewModel,因为它们的 作用域(this)不一致
  1. 作用域的本质
  • ViewModel 的作用域取决于 ViewModelStoreOwner,而 this(调用 ViewModelProvider(this) 中的 this)决定了当前的ViewModelStoreOwner
  • 父Fragment子Fragment 是独立的 ViewModelStoreOwner,所以它们不会共用同一个 ViewModel

二、如何实现父Fragment和子Fragment共用一个ViewModel?

如果我们希望父Fragment和子Fragment共享ViewModel,我们需要让它们的作用域一致。

实现方法 1:以父Fragment作为作用域

在子Fragment中,通过父Fragment作为 ViewModelStoreOwner,这样就可以与父Fragment共用同一个ViewModel。

具体实现
  1. 在父Fragment中创建ViewModel
public class ParentFragment extends Fragment {
    private SharedViewModel sharedViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_parent, container, false);
        // 以 this (父Fragment) 为作用域,创建ViewModel
        sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);
        return view;
    }
}
  1. 在子Fragment中获取父Fragment的ViewModel
public class ChildFragment extends Fragment {
    private SharedViewModel sharedViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_child, container, false);
        // 以父Fragment为作用域,获取ViewModel
        sharedViewModel = new ViewModelProvider(requireParentFragment()).get(SharedViewModel.class);
        return view;
    }
}
解释
  • 在父Fragment中,new ViewModelProvider(this) 使ViewModel的作用域为当前的父Fragment
  • 在子Fragment中,new ViewModelProvider(requireParentFragment()) 指定了子Fragment的ViewModel作用域为父Fragment,所以父子Fragment共用了同一个ViewModel实例。

实现方法 2:以Activity作为作用域(全局共享ViewModel)

如果希望Activity中的多个Fragment都共用一个ViewModel,可以将 Activity 作为作用域。

具体实现
  1. 在父Fragment中获取ViewModel
public class ParentFragment extends Fragment {
    private SharedViewModel sharedViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_parent, container, false);
        // 以 Activity 作为作用域,获取ViewModel
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        return view;
    }
}
  1. 在子Fragment中获取相同的ViewModel
public class ChildFragment extends Fragment {
    private SharedViewModel sharedViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_child, container, false);
        // 以 Activity 作为作用域,获取ViewModel
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        return view;
    }
}
解释
  • ViewModelProvider(requireActivity()) 使得Activity作用域内的所有Fragment共用同一个ViewModel。
  • 这种方式常用于在多个Fragment之间共享数据,适用于ViewPager+Fragment导航页面间数据同步的场景。

三、ViewModel作用域的对比

作用域

ViewModelProvider的调用

共享范围

典型使用场景

当前Fragment

ViewModelProvider(this)

仅当前Fragment内部

当前Fragment内部状态管理

父Fragment

ViewModelProvider(requireParentFragment())

父Fragment和子Fragment

父子Fragment共享数据

Activity

ViewModelProvider(requireActivity())

Activity和所有Fragment

多Fragment共享数据,ViewPager页面

Application

AndroidViewModel (基于App)

所有Activity和Fragment

App全局的ViewModel状态


四、常见的使用场景

场景

作用域选择

ViewModelProvider的调用

示例

父Fragment和子Fragment共享数据

父Fragment

ViewModelProvider(requireParentFragment())

父子页面数据联动,父控子

多个Fragment共享数据

Activity

ViewModelProvider(requireActivity())

ViewPager页面,Activity数据同步

Fragment内部状态管理

当前Fragment

ViewModelProvider(this)

单独页面的ViewModel

全局数据管理

Application

AndroidViewModel

全局登录状态管理


五、常见的注意事项

  1. 不要滥用作用域
  • 如果不需要跨Fragment共享数据,尽量使用 ViewModelProvider(this),使ViewModel的作用域最小化,避免不必要的内存泄漏。
  1. 避免不必要的作用域冲突
  • 如果父Fragment和子Fragment要共用ViewModel,应该显式地使用 requireParentFragment() 作为作用域,而不是默认的 this
  1. ViewModelStoreOwner 的概念
  • ViewModel的共享依赖于 ViewModelStoreOwner,这可以是 Activity、Fragment 或 Application
  • ViewModelStoreOwner 决定 ViewModel 的作用域。

六、总结

问题

解答

父Fragment和子Fragment是否共用ViewModel?

默认不会共用,因为它们的ViewModelStoreOwner不同

如何让父子Fragment共用ViewModel?

在子Fragment中调用 ViewModelProvider(requireParentFragment())

如何让多个Fragment共用ViewModel?

在Fragment中调用 ViewModelProvider(requireActivity()) 使ViewModel作用域为Activity

ViewModel的作用域如何选择?

作用域应根据数据共享范围选择,作用域越大,数据的生命周期越长。


写在最后

  1. 父Fragment和子Fragment默认不会共用ViewModel,但我们可以通过 requireParentFragment() 来实现共用。
  2. 如果想让多个Fragment共用一个ViewModel,最简单的办法是通过 requireActivity() 作为作用域。
  3. 选择合适的作用域,不要滥用共享ViewModel,以防止内存泄漏和数据冗余。

希望这篇文章能帮助你掌握ViewModel的作用域与共享机制!如果你有任何疑问,欢迎在评论区留言交流! 🚀🚀🚀