I have a fragment which shows images and possible sub-albums of an album. The fragment has a single argument which is the ID of the album to show. The ID is a POD long
. For navigation incl. proper navigation history and passing arguments to the fragment, I use the navigation graph and the SafeArgs library.
Clicking on a sub-album navigates to the same fragment with the ID of sub-album to show. The loading of images and sub-albums is done in a view model.
Question: How do I pass the ID of the album as an argument of the constructor to my view model.
Here are some code snippets which might help to understand the problem
The fragment
public class AlbumFragment extends Fragment implements Observer<List<AlbumEntry>> {
private AlbumViewModel viewModel;
private long albumID; /*< The ID of the album which is shown by this instance of the fragment */
public static AlbumFragment newInstance( final long albumID ) {
AlbumFragment fragment = new AlbumFragment();
// The class `AlbumFragmentArgs` is auto-generated by the SafeArgs library
fragment.setArguments(
(new AlbumFragmentArgs.Builder()).setAlbumID( albumID ).build().toBundle()
);
return fragment;
}
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
albumID = AlbumFragmentArgs.fromBundle( getArguments() ).getAlbumID();
// TODO: We somehow need to pass the ID of the album to the view model
viewModel = new ViewModelProvider( this ).get( AlbumViewModel.class );
}
@Override
public View onCreateView( LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState ) {
// Inflates the view from XML layout file, skipped here
return view;
}
@Override
public void onViewCreated( @NonNull final View view, final Bundle savedInstanceState ) {
// PROBLEM: The list of album entries actually depends on the current album
// The ID of the current album should have been passed when then model was created
LiveData<? extends List<AlbumEntry>> liveAlbumEntries = viewModel.getAlbumEntries();
liveAlbumEntries.observe( this.getViewLifecycleOwner(), this );
}
// Methods for observing live data skipped here
protected void onClick( final View view ) {
// The code how the ID of the target album is obtained from the
// clicked view is left out here
// The variable is targetAlbumID
NavGraphDirections.ActionToAlbumFragment action = NavGraphDirections.actionToAlbumFragment();
action.setAlbumID( targetAlbumID );
Navigation.findNavController( this.getView() ).navigate( action );
}
}
The view model
public class AlbumViewModel extends AndroidViewModel {
private long albumID; /*< The ID of the album which is shown by this instance of the fragment */
public AlbumViewModel( Application application /*, final long albumID */ ) {
super( application );
// TODO: How to write a constructor which gets an Application object and an albumID?
/* this.albumID = albumID */
// Remaining code, esp. initialization of the repository, is left out
}
public LiveData<? extends List<AlbumEntry>> getAlbumEntries() {
// Code left out, but depends on `this.albumID`
}
}
Some final remarks
Of course, it might be possible to not pass the ID of the current album to the view model, but always pass the album ID explicitly as an additional argument to each method of the view model, for example LiveData<? extends List<AlbumEntry>> liveAlbumEntries = viewModel.getAlbumEntries( albumID )
.
However, having one instance of the view model for each album ID would be much easier especially with respect to caching. If a single instance of the view model is bound to a particular album ID, the view model only needs to deal with the album entries of this album. If the corresponding fragment for the same album ID ends its life-cycle permanently, the corresponding view model will be destroyed and hence the album entries are removed from memory. If the fragment is only re-created (due to configuration changes) the corresponding view model is kept in memory and so are the album entries.
Contrary, if I only had one application-wide global instance of AlbumViewModel
for all albums and all instances of the fragment and if the album ID is passed explicitly to each method of AlbumViewModel
I would never know, when a particular fragment is destroyed and when the album entries can be removed from memory.