How to generate composed view models with temporary sub views?

笑着哭i 提交于 2019-12-09 21:39:41

问题


Scenario

I have a quizz generator, which generates a sequence of quizzes of different classes. The sequence is of unlimited length.

There is a view model for the quizz generator. There is a view model for each type of a quizz. The quizz generator view model should create the view models of the quizzes depending on their classes.

Issue

A view model must not hold a reference to the lifecycle, but I need the lifecycle to create view models.

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

Questions

Where do I create the sub view models of the quizzes?

The one solution I can think of is to inject the sub view model from the activity each time. This is a detour, especially if nested views are involved.

The other solution is to create the view model in the nested view, which seems ugly either, as it is not common practice to access the lifecycle from inside a view.

If there is no clean solution, what's wrong with my approach of architecture? Should I use fragments for this kind of scenario?


回答1:


I give a first answer by myself inspired by pskink. Maybe I will update the answer after some experiences with the suggested approach.

If a view model shall generate child objects aka components, the components don't need to be bound to the lifecycle themselves as long a the master object is bound to the lifecycle and the components are referenced from the master.

For the given example this means a good place to create the master object is in the top level position of the activity, where the lifecycle is directly available. The quizz objects are referenced from the master objects. They don't need direct access to the lifecycle and may be created anywhere, for example inside the master object. This enables to create them on demand.

The components may or may not be a subclass of ViewModel. I think it a good practice to extend ViewModel. This parent class brings in the onCleared method. This is the place to remove observers from the underlying model. Without doing this, you likely create memory leaks.

You have to take care to call onCleared at the right moments, at least from the onCleared method of the master object. In this special case each previous quizz has to be cleared just before a new quizz is generated, to remove the references from the underlying quizz models.

The view models of the components can simply be created using the new keyword. There is no need to use factory or a provider.




回答2:


If there is no clean solution, what's wrong with my approach of architecture? Should I use fragments for this kind of scenario?

Yes, fragments are the right choice

Summary:

  1. There is no real alternative to binding Views by LiveData.
  2. If using LiveData a LifeCycle is required.
  3. If the lifecycle of a sub views in a sequence should be shorter than the lifecycle of the activity, then fragments are the way to go.

Details

There is no real alternative to binding Views by LiveData.

View models should not hold unterminated references to views, else the views exists as long as the view model exists causing memory leaks. There are three observer patterns to discuss how views could observe view models.

a.) MutableLiveData

They require a lifecycle. The references are cleaned up automatically, when the lifecycle ends. This is the recommended solution.

b.) WeakReferences

In theory this should work. The weak reference should be cleaned up by the garbage collector, when the last hard reference to the view is gone. In practice the solution is unstable and references sometime go away prematurely.

c.) Handmade observer

A handmade observer must call a remove method. Unfortunately there is no defined destruction hook, when a view goes away. There is no place to call the remove method in a view.

As a result a.) is the only possible solution according to my experience.

As a lifecycle is required for LiveData fragments are the way to go

The sub views mentioned here are created in a sequence. If we would bind them to the activity, they would pile up until the activity goes away although they are only needed for a small interval of time in sequence.

Fragments can exist for a subpart of the time of the activity. They are the right solution to bind the sub views of the sequence to them.

Example code

The quizzes are called challenges here. The FragmentManger is always that of the activity, while the LifecycleOwner is either the activity or a fragment.

# 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> {
  [...]
}


来源:https://stackoverflow.com/questions/49894570/how-to-generate-composed-view-models-with-temporary-sub-views

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!