I was looking at the BasicSample app from Android Architecture components sample. In the ProductViewModel.java file, some comments read:
It's not actually necessary in this case, as the product ID can be passed in a public method.
Based on my understanding of the comment, I would like to know if it's possible to pass the productId to the ProductViewModel without using a factory, and how this can be done.
I have implemented Transformation, and I know I can pass a productId using switchMap. But I was looking for a way to initialize the model with a single id.
public class ProductViewModel extends AndroidViewModel {
private final LiveData<ProductEntity> mObservableProduct;
public ObservableField<ProductEntity> product = new ObservableField<>();
private final int mProductId;
private final LiveData<List<CommentEntity>> mObservableComments;
public ProductViewModel(@NonNull Application application, DataRepository repository,
final int productId) {
super(application);
mProductId = productId;
mObservableComments = repository.loadComments(mProductId);
mObservableProduct = repository.loadProduct(mProductId);
}
....
/**
* A creator is used to inject the product ID into the ViewModel
* <p>
* This creator is to showcase how to inject dependencies into ViewModels. It's not
* actually necessary in this case, as the product ID can be passed in a public method.
*/
public static class Factory extends ViewModelProvider.NewInstanceFactory {
@NonNull
private final Application mApplication;
private final int mProductId;
private final DataRepository mRepository;
public Factory(@NonNull Application application, int productId) {
mApplication = application;
mProductId = productId;
mRepository = ((BasicApp) application).getRepository();
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
//noinspection unchecked
return (T) new ProductViewModel(mApplication, mRepository, mProductId);
}
}
}
In reference to the sample, when the comment says:
the product ID can be passed in a public method
this is referring to the fact you can create a public setter method.
Since the productId
is used to get a LiveData from your database, you should use a switchMap
, as you mentioned. This is because switchMap
allows you to lookup and update what a LiveData
is pointing to, without needing to re-setup observers. If you didn't use a switchMap
, you'd need to tell your Activity to observe the newly looked-up LiveData
, and potentially stop observing the old LiveData
object. More description of this is included in the docs.
One more note - the factory is also useful here because you're passing in or injecting the DataRepository
dependency via the constructor. This is a preferable way to get the repository into the class because it's easy to mock the repository when testing.
With that in mind, if you wanted to do this would a factory, your code might look something like:
public class ProductViewModel extends AndroidViewModel {
private final LiveData<ProductEntity> mProduct;
private final LiveData<List<CommentEntity>> mComments;
private final MutableLiveData<Integer> mProductId = new MutableLiveData<>();
public ProductViewModel(@NonNull Application application) {
super(application);
// Have some way to get your repository, this is not great for testing...
Repository repository = ((BasicApp) application).getRepository();
mProduct = Transformations.switchMap(mProductId, id -> {
return repository.loadProduct(id);
}
mComments = Transformations.switchMap(mComments, id -> {
return repository.loadComments(id);
}
}
public void setProductId(int productId) {
mProductId.setValue(productId); // This will trigger both of those switchMap statements
}
}