简体   繁体   中英

Android mvvm livedata not observing

This is my first time using MVVM architecture.I am also using LiveData . I simply retrieve data from server using Retrofit.So upon clicking a button in the View(MainActivity.class) I invoke the ViewModel class's method(handleRetrofitcall()) to take up the duty of Api calling from the Model class(Retrofit Handler.class) .The Model class upon retrieving the data informs the ViewModel of the data(which is actually the size of items).I set the size to LiveData and try to listen for it.Unfortunately I couldn't.For detailed analysis please go through the code.

Model...

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

}

View.....

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

You're creating a new ViewModel in the RetrofitHandler, so nothing is observing that viewmodel. Instead of having the RetrofitHandler rely on a ViewModel internally, it's probably safer to handle the Retrofit callback inself, and post data there.

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.
}

Edit: More clarification.

In the Activity, you're correctly creating a ViewModel and observing it (we'll call that ViewModel A). ViewModel A is then creating a RetrofitHandler and calling getData on that Retrofithandler. The issue is that RetrofitHandler is creating a new ViewModel in getData (which I'm going to call ViewModel B). The issue is that the results are being posted to ViewModel B, which nothing is observing, so it seems like nothing is working.

Easy way to avoid this issue is to make sure that only an Activity/Fragment is relying on (and creating) ViewModels. Nothing else should know about the ViewModel.

Edit 2: Here's a simple implementation. I haven't tested it, but it should be more or less correct.

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

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM