ViewModelProviders с Dagger 2, не в состоянии понять концепцию - PullRequest
0 голосов
/ 04 июня 2018

У меня есть сервис Retrofit, подобный этому

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;

    }
}

Я использую конструктор Dagger2 путем инъекцииМодифицировать так.Затем у меня есть 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;
    }


}

Чтобы внедрить Retrofit в BrandsRepository, я должен внедрить 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.Это работает нормально, но когда я пытаюсь использовать ViewModelProviders, раскомментировав его, кинжал выдает ошибку, которая очевидна.Правильный способ получить ViewModel - использовать ViewModelProviders, но как мне это сделать, внедряя модификацию таким образом.

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

Ответ может быть проще, чем подход Муми, который заключается в том, что вы представляете ViewModel для своего компонента:

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

И теперь вы можете получить доступ к этому методу в компоненте внутри ViewModelFactory:

// @Inject
BrandsViewModel brandsViewModel;

...
brandsViewModel = ViewModelProviders.of(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 ->
    ViewModelProviders.of(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 может получать свои параметрыиз кинжала:

class BrandsViewModel @Inject constructor(
    private val appContext: Context,
    /* other deps */
): ViewModel() {
    ...
}
0 голосов
/ 04 июня 2018

Ответ основан на android-архитектура-компоненты .

Вы можете использовать привязку карты в Dagger.

Во-первых, объявить ключ карты следующим образом.

@MustBeDocumented
@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

Во-вторых, для создания карты.

@Module
abstract class ViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(UserViewModel::class)
    abstract fun bindUserViewModel(userViewModel: UserViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(SearchViewModel::class)
    abstract fun bindSearchViewModel(searchViewModel: SearchViewModel): ViewModel
}

Далее, чтобы создать заводской файл для обработки карты, который использует ключ для выбора ViewModel.

@Singleton
class GithubViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

Наконец, чтобы добавить фабрику в вашу деятельность или фрагмент.

class SearchFragment : Fragment(), Injectable {

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        searchViewModel = ViewModelProviders.of(this, viewModelFactory)
            .get(SearchViewModel::class.java)
}

Таким образом, вы можете внедрить репозиторий в вашу ViewModel.

class SearchViewModel @Inject constructor(repoRepository: RepoRepository) : ViewModel() {
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...