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:
- There is no real alternative to binding
Views
by LiveData
.
- If using
LiveData
a LifeCycle
is required.
- 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> {
[...]
}
new ViewModelProvider
? – BrittneybrittniViewModel
to be bound to your "master"ViewModel
, right? – BrittneybrittniViewModel
. – TwitteryHow to generate view models from inside a view model?
so i dont get what you really need – Brittneybrittni