Андроид мввм ливата не соблюдает
Я впервые использую архитектуру MVVM. Я также использую LiveData. Я просто извлекаю данные с сервера с помощью Retrofit. Поэтому при нажатии кнопки в представлении (MainActivity.class) я вызываю метод класса ViewModel (handleRetrofitcall()), чтобы взять на себя обязанность вызова API из класса Model (Retrofit Handler.class.). Класс Model после извлечения данных информирует ViewModel о данных (что на самом деле является размером элементов). Я устанавливаю размер в LiveData и пытаюсь его прослушать. К сожалению, я не смог. Для подробного анализа, пожалуйста, пройдите код.
Модель...
RetrofitHandler.class:
public class RetrofitHandler {
private ApiInterface apiInterface;
private SimpleViewModel viewModel;
public void getData(){
apiInterface= ApiClient.getClient().create(ApiInterface.class);
Call<Unknownapi> call=apiInterface.doGetListResources();
call.enqueue(new Callback<Unknownapi>() {
@Override
public void onResponse(Call<Unknownapi> call, Response<Unknownapi> response) {
List<Unknownapi.Data> list;
Unknownapi unknownapi=response.body();
list=unknownapi.getData();
viewModel=new SimpleViewModel();
viewModel.postValue(list.size());
Log.e("Size",Integer.toString(list.size()));
}
@Override
public void onFailure(Call<Unknownapi> call, Throwable t) {
}
});
}
}
ViewModel....
SimpleViewModel.class:
public class SimpleViewModel extends ViewModel {
private RetrofitHandler retrofitHandler;
private int size;
private MutableLiveData<Integer> mutablesize=new MutableLiveData<>();
public SimpleViewModel() {
super();
}
@Override
protected void onCleared() {
super.onCleared();
}
public void handleRetrofitcall(){
retrofitHandler=new RetrofitHandler();
retrofitHandler.getData();
}
public void postValue(int size){
this.size=size;
mutablesize.postValue(this.size);
Log.e("lk","f");
}
public MutableLiveData<Integer> getObject() {
return mutablesize;
}
}
Посмотреть.....
MainActivity.class:
public class MainActivity extends AppCompatActivity {
private TextView status;
private SimpleViewModel viewModel;
private Observer<Integer> observer;
private MutableLiveData<Integer> mutableLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
status=findViewById(R.id.status);
viewModel=ViewModelProviders.of(MainActivity.this).get(SimpleViewModel.class);
observer=new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer integer) {
Log.e("lk","f");
status.setText(Integer.toString(integer));
}
};
viewModel.getObject().observe(MainActivity.this,observer);
findViewById(R.id.retrofit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.handleRetrofitcall();
}
});
}
@Override
protected void onDestroy() {
if (observer!=null){
viewModel.getObject().removeObserver(observer);
}
super.onDestroy();
}
}
1 ответ
Вы создаете новую ViewModel в RetrofitHandler, поэтому ничто не наблюдает за этой моделью представления. Вместо того, чтобы RetrofitHandler полагался на ViewModel внутренне, вероятно, безопаснее обрабатывать обратный вызов Retrofit и размещать там данные.
public void handleRetrofitcall(){
retrofitHandler=new RetrofitHandler();
retrofitHandler.getData(new Callback<List<Unknownapi.Data>> {
// add actual callback implementation here
); // add a callback here, so that the data is available in the view model. Then post the results from here.
}
Изменить: больше разъяснений.
В Activity вы правильно создаете ViewModel и наблюдаете за ним (назовем это ViewModel A). ViewModel A затем создает RetrofitHandler и вызывает getData
на этом Retrofithandler. Проблема в том, что RetrofitHandler создает новую модель представления в getData
(который я собираюсь назвать ViewModel B). Проблема заключается в том, что результаты публикуются во ViewModel B, который ничего не наблюдает, поэтому кажется, что ничего не работает.
Простой способ избежать этой проблемы - убедиться, что только Activity/Fragment полагается (и создает) на ViewModels. Больше ничего не должно знать о ViewModel.
Редактировать 2: Вот простая реализация. Я не проверял это, но это должно быть более или менее правильно.
// shouldn't know anything about the view model or the view
public class RetrofitHandler {
private ApiInterface apiInterface;
// this should probably pass in a different type of callback that doesn't require retrofit
public void getData(Callback<Unknownapi> callback) {
// only create the apiInterface once
if (apiInterface == null) {
apiInterface = ApiClient.getClient().create(ApiInterface.class);
}
// allow the calling function to handle the result
apiInterface.doGetListResources().enqueue(callback);
}
}
// shouldn't know how retrofit handler parses the data
public class SimpleViewModel extends ViewModel {
private RetrofitHandler retrofitHandler = new RetrofitHandler();
// store data in mutableSize, not with a backing field.
private MutableLiveData<Integer> mutableSize = new MutableLiveData<>();
public void handleRetrofitCall() {
// handle the data parsing here
retrofitHandler.getData(new Callback<Unknownapi>() {
@Override
public void onResponse(Call<Unknownapi> call, Response<Unknownapi> response) {
Unknownapi unknownapi = response.body();
int listSize = unknownapi.getData().size;
// set the value of the LiveData. Observers will be notified
mutableSize.setValue(listSize); // Note that we're using setValue because retrofit callbacks come back on the main thread.
Log.e("Size", Integer.toString(listSize));
}
@Override
public void onFailure(Call<Unknownapi> call, Throwable t) {
// error handling should be added here
}
});
}
// this should probably return an immutable copy of the object
public MutableLiveData<Integer> getObject() {
return mutableSize;
}
}
public class MainActivity extends AppCompatActivity {
private TextView status;
// initialize the view model only once
private SimpleViewModel viewModel = ViewModelProviders.of(MainActivity.this).get(SimpleViewModel.class);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
status = findViewById(R.id.status);
// observe the view model's changes
viewModel.getObject().observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer integer) {
// you should handle possibility of interger being null
Log.e("lk","f");
status.setText(Integer.toString(integer));
}
});
findViewById(R.id.retrofit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// call the view model's function
viewModel.handleRetrofitCall();
}
});
}
}