16

I'm currently transforming my project architecture from MVP to MVVM. When I'm working on it, I find something made me confused:

In ScheduleViewModelFactory.kt of project iosched, the factory implements ViewModelProvider.Factory:

class ScheduleViewModelFactory(
    private val userEventRepository:DefaultSessionAndUserEventRepository
) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ScheduleViewModel::class.java)) {
            return ScheduleViewModel(LoadUserSessionsByDayUseCase(userEventRepository)) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

In DetailViewModelFactory.java of project Sunshine from codelab, the factory extends ViewModelProvider.NewInstanceFactory:

public class DetailViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private final SunshineRepository mRepository;
    private final Date mDate;

    public DetailViewModelFactory(SunshineRepository repository, Date date) {
        this.mRepository = repository;
        this.mDate = date;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        //noinspection unchecked
        return (T) new DetailActivityViewModel(mRepository, mDate);
    }
}

I would like to know:

  1. What are the differences between ViewModelProvider.Factory and ViewModelProvider.NewInstanceFactory?
  2. Why they are being used like the codes mentioned above?
  3. What is the best practice/scenario to use each of them?
Carter Chen
  • 792
  • 1
  • 8
  • 22

2 Answers2

5

What are the differences between ViewModelProvider.Factory and ViewModelProvider.NewInstanceFactory?

Why they are being used like the codes mentioned above?

Basing on ViewModelProvider documentation:

public class ViewModelProvider {
    /**
     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
     */
    public interface Factory {
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    (...)


    /**
     * Simple factory, which calls empty constructor on the given class.
     */
    public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }
    (...)
}

and taking geeksforgeeks description of newInstance() into consideration:

In general, new operator is used to create objects, but if we want to decide type of object to be created at runtime, there is no way we can use new operator. In this case, we have to use newInstance() method.

I assume the NewInstanceFactory is an implementation of the Factory, which can be used when we want to create ViewModels of different types.


On the other hand, in google's android-architecture/todoapp there is:

public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    (...)
    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (modelClass.isAssignableFrom(StatisticsViewModel.class)) {
            //noinspection unchecked
            return (T) new StatisticsViewModel(mApplication, mTasksRepository);
        } else if (modelClass.isAssignableFrom(TaskDetailViewModel.class)) {
            //noinspection unchecked
            return (T) new TaskDetailViewModel(mApplication, mTasksRepository);
        } else if (modelClass.isAssignableFrom(AddEditTaskViewModel.class)) {
            //noinspection unchecked
            return (T) new AddEditTaskViewModel(mApplication, mTasksRepository);
        } else if (modelClass.isAssignableFrom(TasksViewModel.class)) {
            //noinspection unchecked
            return (T) new TasksViewModel(mApplication, mTasksRepository);
        }
        throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass.getName());
    }
}

They are using the NewInstanceFactory, but are overriding the create method! To my understanding, if we override it, there's no difference from using a regular Factory.

Steru
  • 53
  • 1
  • 6
  • " but are overriding the create method! To my understanding, if we override it, there's no difference from using a regular Factory” -- why do you say this? – Sakiboy Sep 25 '19 at 14:24
  • 2
    because the only thing the `NewInstanceFactory` does, it is implementing the `create()` method. If google is using the `NewInstanceFactory` but are overriding the `create()` method, they could've been using the regular `Factory` in the first place. – Steru Oct 28 '19 at 13:08
1

ViewModelProvider.Factory is responsible to create your instance of ViewModel.

If your ViewModel have dependencies and you want to test your ViewModel then you should create your own ViewModelProvider.Factory and passed dependency through ViewModel constructor and give value to the ViewModelProvider.Factory instance.

When to use ViewModelProvider.Factory?

If your ViewModel have dependencies then you should pass this dependencies through the constructor (It is the best way to pass your dependencies), so you can mock that dependencies and test your ViewModel.

When not to use ViewModelProvider.Factory

If your ViewModel have no dependencies then you will not require to create your own ViewModelProvider.Factory. The default implementation is enough to create ViewModel for you.

Please go through this blog for details.

livemaker
  • 464
  • 1
  • 9
  • 19