带有Dagger 2的ViewModelProviders无法掌握概念 [英] ViewModelProviders with Dagger 2, not able to grasp the concept
问题描述
我有这样的改造服务
public interface BrandsService {
@GET("listBrand")
Call<List<Brand>> getBrands();
}
然后我有一个存储库,可以从这样的api获取数据
Then I have a Repository to get data from an api like this
public class BrandsRepository {
public static final String TAG = "BrandsRepository";
MutableLiveData<List<Brand>> mutableLiveData;
Retrofit retrofit;
@Inject
public BrandsRepository(Retrofit retrofit) {
this.retrofit = retrofit;
}
public LiveData<List<Brand>> getListOfBrands() {
// Retrofit retrofit = ApiManager.getAdapter();
final BrandsService brandsService = retrofit.create(BrandsService.class);
Log.d(TAG, "getListOfBrands: 00000000000 "+retrofit);
mutableLiveData = new MutableLiveData<>();
Call<List<Brand>> retrofitCall = brandsService.getBrands();
retrofitCall.enqueue(new Callback<List<Brand>>() {
@Override
public void onResponse(Call<List<Brand>> call, Response<List<Brand>> response) {
mutableLiveData.setValue(response.body());
}
@Override
public void onFailure(Call<List<Brand>> call, Throwable t) {
t.printStackTrace();
}
});
return mutableLiveData;
}
}
我正在通过注入Retrofit来使用Dagger2的构造函数注入. 然后我有一个这样的ViewModel
I am using constructor injection of Dagger2 by injecting Retrofit like that. Then I have a ViewModel like this
public class BrandsViewModel extends ViewModel{
BrandsRepository brandsRepository;
LiveData<List<Brand>> brandsLiveData;
@Inject
public BrandsViewModel(BrandsRepository brandsRepository) {
this.brandsRepository = brandsRepository;
}
public void callService(){
brandsLiveData = brandsRepository.getListOfBrands();
}
public LiveData<List<Brand>> getBrandsLiveData() {
return brandsLiveData;
}
}
要在BrandsRepository中注入改造,我必须像这样注入BrandsRepository. 然后我有这样的MainActivity
To inject Retrofit in BrandsRepository, I have to inject BrandsRepository like that. Then I have MainActivity like this
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Inject
BrandsViewModel brandsViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MainApplication)getApplication()).getNetComponent().inject(this);
// BrandsViewModel brandsViewModel = ViewModelProviders.of(this).get(BrandsViewModel.class);
brandsViewModel.callService();
LiveData<List<Brand>> brandsLiveData = brandsViewModel.getBrandsLiveData();
brandsLiveData.observe(this, new Observer<List<Brand>>() {
@Override
public void onChanged(@Nullable List<Brand> brands) {
Log.d(TAG, "onCreate: "+brands.get(0).getName());
}
});
}
}
使用Dagger2而非ViewModelProviders注入BrandsViewModel.这工作正常,但是当我尝试通过取消注释使用ViewModelProviders时,匕首给我的错误是显而易见的.获取ViewModel的正确方法是使用ViewModelProviders,但是在注入这样的改造时,我将如何做到这一点.
The BrandsViewModel is injected using Dagger2 rather than ViewModelProviders. This is working fine but when I try to use ViewModelProviders by uncommenting it, dagger gives me the error which is obvious. The proper way of getting a ViewModel is by using ViewModelProviders but how will I accomplish this while injecting retrofit like that.
推荐答案
编辑:重要说明.要使用Jetpack ViewModel,您不需要地图多重绑定.请继续阅读.
An important note. To use Jetpack ViewModel, you don't need map-multibinding. Read on.
答案可能比Mumi的方法更简单,后者是在组件上公开ViewModel:
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();
}
现在您可以在ViewModelFactory内部的组件上访问此方法:
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 <T extends ViewModel> create(Class<T> modelClazz) {
if(modelClazz == BrandsViewModel.class) {
return singletonComponent.brandsViewModel();
}
throw new IllegalArgumentException("Unexpected class: [" + modelClazz + "]");
}).get(BrandsViewModel.class);
所有这些都可以使用Kotlin进行简化和隐藏:
All this can be simplified and hidden with Kotlin:
inline fun <reified T: ViewModel> AppCompatActivity.createViewModel(crossinline factory: () -> T): T = T::class.java.let { clazz ->
ViewModelProvider(this, object: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass == clazz) {
@Suppress("UNCHECKED_CAST")
return factory() as T
}
throw IllegalArgumentException("Unexpected argument: $modelClass")
}
}).get(clazz)
}
现在可以让您这样做
brandsViewModel = createViewModel { singletonComponent.brandsViewModel() }
现在BrandsViewModel
可以从Dagger接收其参数:
Where now BrandsViewModel
can receive its parameters from Dagger:
class BrandsViewModel @Inject constructor(
private val appContext: Context,
/* other deps */
): ViewModel() {
...
}
如果从Dagger中公开了Provider<BrandsViewModel>
,则意图可能会更清晰
Though the intent might be cleaner if a Provider<BrandsViewModel>
is exposed from Dagger instead
interface SingletonComponent {
fun brandsViewModel(): Provider<BrandsViewModel>
}
brandsViewModel = createViewModel { singletonComponent.brandsViewModel().get() }
android-ktx
带来了一些额外的诡计,您甚至可以做
With some additional trickery coming in from android-ktx
, you could even do
@Suppress("UNCHECKED_CAST")
inline fun <reified T : ViewModel> Fragment.fragmentViewModels(
crossinline creator: () -> T
): Lazy<T> {
return createViewModelLazy(T::class, storeProducer = {
viewModelStore
}, factoryProducer = {
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(
modelClass: Class<T>
): T = creator.invoke() as T
}
})
}
然后
class ProfileFragment: Fragment(R.layout.profile_fragment) {
private val viewModel by fragmentViewModels {
singletonComponent.brandsViewModelFactory().get()
}
brandsViewModelFactory()
所在的位置
fun brandsViewModelFactory(): Provider<BrandsViewModel>
这篇关于带有Dagger 2的ViewModelProviders无法掌握概念的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!