android - 带有 Dagger 2 的 ViewModelProviders,无法掌握概念

标签 android retrofit dagger-2 android-architecture-components android-viewmodel

我有这样的改造服务

public interface BrandsService {
    @GET("listBrand")
    Call<List<Brand>> getBrands();
}

然后我有一个存储库来从这样的 api 获取数据

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;

    }
}

我正在通过注入(inject) Retrofit 来使用 Dagger2 的构造函数注入(inject)。 然后我有一个像这样的 ViewModel

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 中注入(inject) Retrofit,我必须像那样注入(inject) BrandsRepository。 然后我有这样的 MainActivity

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());
            }
        });


    }
}

BrandsViewModel 是使用 Dagger2 而不是 ViewModelProviders 注入(inject)的。这工作正常但是当我尝试通过取消注释来使用 ViewModelProviders 时,dagger 给我一个明显的错误。获取 ViewModel 的正确方法是使用 ViewModelProviders,但我将如何在注入(inject)这样的改造时实现这一点。

最佳答案

编辑:重要说明。要使用 Jetpack ViewModel,您不需要 map-multibinding。继续阅读。


答案可能比 Mumi 的方法更简单,即在组件上公开 ViewModel:

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

现在您可以在 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 进行简化和隐藏:

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 接收它的参数:

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

尽管如果 Provider<BrandsViewModel> 的 Intent 可能更清晰而是从 Dagger 暴露

interface SingletonComponent {
    fun brandsViewModel(): Provider<BrandsViewModel>
}

brandsViewModel = createViewModel { singletonComponent.brandsViewModel().get() }

来自 android-ktx 的一些额外技巧, 你甚至可以做

@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>

关于android - 带有 Dagger 2 的 ViewModelProviders,无法掌握概念,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50673266/

相关文章:

android - 从 AppWidgetProvider 中的 strings.xml 中获取字符串

android - 使用 WAKE_LOCK 停止服务

java - 应用关闭后未收到推送

java - 使用 Retrofit 和 GSON 解析 JSON,尝试解析和获取回调时出错。

java - 使用Kotlin创建自定义Dagger 2示波器

android - Dagger 2 Android 没有注入(inject)字段

android - 使用 Intent.ACTION_CALL 拨号停止在电话号码中的 #

android - OkHttp + 改造离线缓存不起作用

java - 没有从 Retrofit 获得任何数据

java - Dagger 2 注入(inject)没有模块的单例