【问题标题】:How to generate composed view models with temporary sub views?如何生成具有临时子视图的组合视图模型?
【发布时间】:2018-04-18 08:16:08
【问题描述】:

场景

我有一个测验生成器,它生成一系列不同类别的测验。序列不限长度。

测验生成器有一个视图模型。每种类型的测验都有一个视图模型。测验生成器视图模型应根据测验的类创建测验的视图模型。

问题

视图模型不能包含对生命周期的引用,但我需要生命周期来创建视图模型。

ViewModelProviders.of(lifecycle).get(classForQuizzType);

问题

在哪里创建测验的子视图模型?

我能想到的一种解决方案是每次都从活动中注入子视图模型。这是一个弯路,尤其是在涉及嵌套视图的情况下。

另一种解决方案是在嵌套视图中创建视图模型,这看起来也很丑陋,因为从视图内部访问生命周期并不常见。

如果没有干净的解决方案,我的架构方法有什么问题?我应该在这种情况下使用片段吗?

【问题讨论】:

  • 你不能创建一个new ViewModelProvider吗?
  • 视图模型必须绑定到生命周期。
  • 但是您希望您的新测验ViewModel 绑定到您的“大师”ViewModel,对吧?
  • 不一定,但我明白你的意思。只需一个句柄即可将组合绑定到生命周期。组件甚至不需要是 ViewModel
  • 标题说:How to generate view models from inside a view model? 所以我没有得到你真正需要的东西

标签: android android-fragments viewmodel android-viewmodel


【解决方案1】:

pskink 启发,我自己给出了第一个答案。也许我会在对建议的方法进行一些经验后更新答案。

如果视图模型应生成子对象(也称为组件),则组件本身不需要绑定到生命周期,只要主对象绑定到生命周期并且组件是从主对象引用的。

对于给定的示例,这意味着创建主对象的好地方是在活动的顶层位置,lifecycle 可以直接使用。测验对象是从主对象引用的。它们不需要直接访问生命周期,可以在任何地方创建,例如在主对象内部。这样可以按需创建它们。

组件可能是也可能不是ViewModel 的子类。我认为扩展ViewModel 是一个好习惯。这个父类引入了onCleared 方法。这是从底层模型中删除观察者的地方。如果不这样做,您可能会造成内存泄漏。

您必须注意在正确的时刻调用onCleared,至少从主对象的onCleared 方法中调用。在这种特殊情况下,必须在生成新测验之前清除每个先前的测验,以从基础测验模型中删除引用。

可以使用new 关键字简单地创建组件的视图模型。无需使用工厂或提供者。

【讨论】:

  • 没错,您的组件很可能是LiveData 对象的容器,它们不必是ViewModels(除非对此有一些要求)
  • 组件作为 LiveData 对象的容器需要访问 LiveData 对象的生命周期。如果不允许 master 持有对它的引用,从哪里按需获取生命周期?
  • liveData.observe(lifecycle, observer); 与原始问题类似,生命周期仅在顶层可用,但内部视图和内部视图模型均不可用。所以我不能在那些地方创建组件,它们是LiveData 的容器。我需要离开LiveData
  • 如果 LiveData 的功能是自动注销观察者,我想我需要手动注销观察者。
【解决方案2】:

如果没有干净的解决方案,我的方法有什么问题 建筑学?我应该在这种情况下使用片段吗?

是的,片段是正确的选择

总结:

  1. 除了将Views 绑定到LiveData 之外,没有其他真正的替代方法。
  2. 如果使用LiveData,则需要LifeCycle
  3. 如果序列中子视图的生命周期应该比活动的生命周期短,那么片段就是要走的路。

详情

除了将Views 绑定到LiveData 之外,别无选择。

视图模型不应持有对视图的未终止引用,否则只要视图模型存在,视图就会存在,从而导致内存泄漏。共有三种观察者模式来讨论视图如何观察视图模型。

a.) 可变实时数据

它们需要一个生命周期。当生命周期结束时,引用会自动清理。这是推荐的解决方案。

b.) 弱引用

理论上这应该可行。当最后一个对视图的硬引用消失时,垃圾收集器应该清理弱引用。在实践中,解决方案是不稳定的,有时引用会过早地消失。

c.) 手工观察者

手工观察者必须调用 remove 方法。不幸的是,当视图消失时,没有定义的破坏钩子。视图中没有地方可以调用remove方法。

因此,根据我的经验,a.) 是唯一可能的解决方案。

LiveData 片段需要生命周期

这里提到的子视图是按顺序创建的。如果我们将它们绑定到 Activity,它们会堆积起来直到 Activity 消失,尽管它们只需要在序列中的一小段时间间隔内使用。

片段可以存在于活动时间的一小部分。它们是将序列的子视图绑定到它们的正确解决方案。

示例代码

测验在这里被称为挑战。 FragmentManger 始终是活动的,而LifecycleOwner 是活动或片段。

# A view model acceptor interface for views

public interface ViewModelAcceptor<T extends  ViewModel> {
    void plugViewModel(
        T viewModel,
        LifecycleOwner lifecycleOwner,
        FragmentManager fragmentManager
    );
}

# In the parent view class of the challenges new challenges are created
# in sequence

ChallengeFragment challengeFragment = new ChallengeFragment();
challengeFragment.setChallengeViewModel(challengeViewModel);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(this.getId(), challengeFragment);
fragmentTransaction.commit();

# ChallengeFragment

public class ChallengeFragment extends Fragment {

    private ChallengeViewModel challengeViewModel;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return new ChallengeView(getActivity(), null);
    }

    public void setChallengeViewModel(ChallengeViewModel challengeViewModel) {
        this.challengeViewModel = challengeViewModel;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ChallengeView challengeView = (ChallengeView) getView();
        Objects.requireNonNull(challengeView)
                .plugViewModel(challengeViewModel, this, getFragmentManager());
    }

}
# Challenge views are the child views of the sequence

public class ChallengeView extends ConstraintLayout implements ViewModelAcceptor<ChallengeViewModel> {
  [...]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多