ViewModelProviders with Dagger 2, not able to grasp the concept

后端 未结 2 1939
渐次进展
渐次进展 2020-12-09 06:02

I have a Retrofit Service like this

public interface BrandsService {
    @GET(\"listBrand\")
    Call> getBrands();
}
2条回答
  •  佛祖请我去吃肉
    2020-12-09 06:35

    EDIT: An important note. To use Jetpack ViewModel, you don't need map-multibinding. Read on.


    The answer can be simpler than Mumi's approach, which is that you expose the ViewModel on your component:

    @Singleton
    @Component(modules={...})
    public interface SingletonComponent {
        BrandsViewModel brandsViewModel();
    }
    

    And now you can access this method on the component inside the ViewModelFactory:

    // @Inject
    BrandsViewModel brandsViewModel;
    
    ...
    brandsViewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {
        @Override
        public  create(Class modelClazz) {
            if(modelClazz == BrandsViewModel.class) {
                return singletonComponent.brandsViewModel();
            }
            throw new IllegalArgumentException("Unexpected class: [" + modelClazz + "]");
        }).get(BrandsViewModel.class);
    

    All this can be simplified and hidden with Kotlin:

    inline fun  AppCompatActivity.createViewModel(crossinline factory: () -> T): T = T::class.java.let { clazz ->
        ViewModelProvider(this, object: ViewModelProvider.Factory {
            override fun  create(modelClass: Class): T {
                if(modelClass == clazz) {
                    @Suppress("UNCHECKED_CAST")
                    return factory() as T
                }
                throw IllegalArgumentException("Unexpected argument: $modelClass")
            }
        }).get(clazz)
    }
    

    which now lets you do

    brandsViewModel = createViewModel { singletonComponent.brandsViewModel() }
    

    Where now BrandsViewModel can receive its parameters from Dagger:

    class BrandsViewModel @Inject constructor(
        private val appContext: Context,
        /* other deps */
    ): ViewModel() {
        ...
    }
    

    Though the intent might be cleaner if a Provider is exposed from Dagger instead

    interface SingletonComponent {
        fun brandsViewModel(): Provider
    }
    
    brandsViewModel = createViewModel { singletonComponent.brandsViewModel().get() }
    

    With some additional trickery coming in from android-ktx, you could even do

    @Suppress("UNCHECKED_CAST")
    inline fun  Fragment.fragmentViewModels(
        crossinline creator: () -> T
    ): Lazy {
        return createViewModelLazy(T::class, storeProducer = {
            viewModelStore
        }, factoryProducer = {
            object : ViewModelProvider.Factory {
                override fun  create(
                    modelClass: Class
                ): T = creator.invoke() as T
            }
        })
    }
    

    And then

    class ProfileFragment: Fragment(R.layout.profile_fragment) {
        private val viewModel by fragmentViewModels {
            singletonComponent.brandsViewModelFactory().get()
        }
    

    Where brandsViewModelFactory() is

    fun brandsViewModelFactory(): Provider
    

提交回复
热议问题