Home > OS >  LiveData not updated in ViewModel
LiveData not updated in ViewModel

Time:12-10

In my ViewModel class I have a method that fetches data from the internet and sets them on LiveData via setValue() (I've used the approach from these docs):

public class PageViewModel extends ViewModel {  
private MutableLiveData<List<?>> mDataList;
...


private LiveData<List<?> getDataList() {
    if (mDataList == null) {
        mDataList = new MutableLiveData<>();
    }

    Handler handler = new Handler(Looper.getMainLooper());
    // getData() connects to the internet and fetches the online data 
    mInternetConnection.getData(new InternetConnection.ConnectionCallback<List<?>>() {
        @Override
        public void onComplete(Result<List<?>> result) {
            if (result instanceof Result.Success) {
                mDataList.setValue(((Result.Success<List<?>>) result).data);
            } else {
                Log.e(LOG_TAG, "ViewModel could not obtain data list");
            }
        }
    }, handler);
    
    return mDataList;
}

But mDataList.getValue() is null. The setValue() method is called on the main thread. onComplete gets called for sure (checked).

When I check whether the value of mDataList is null in the onComplete method right after setValue(), the logs show that it is not null.

Why is it null and how should you get the value of the modified mDataList?

I've spent hours looking for a solution on the web, but couldn't find anything that could help. The related questions on this site don't help to solve the problem either.

EDIT:

From the docs: In this example, the callback passed into the Repository's makeLoginRequest call is executed on the main thread. That means you can directly modify the UI from the callback or use LiveData.setValue() to communicate with the UI.

So I've followed that, but this part: "or use LiveData.setValue() to communicate with the UI" doesn't work.

The observer is set on it in the Fragment, but LiveData still don't get updated - list in onChanged is null, the screen remains blank, no errors.

Fragment code:

@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    binding = FragmentMainBinding.inflate(inflater, container, false);
    View rootView = binding.getRoot();
    binding.listRecyclv.setLayoutManager(new LinearLayoutManager(rootView.getContext()));
    mAdapter = createAdapter(rootView.getContext(), pageViewModel.getList().getValue());
    binding.listRecyclv.setAdapter(mAdapter);

    pageViewModel.getList().observe(getViewLifecycleOwner(), new Observer<List<?>>() {
    @Override
    public void onChanged(@Nullable List<?> list) {         
            mAdapter.setList((ArrayList<MyObj>) list);
            binding.listRecyclv.setAdapter(mAdapter);
            }
    }
}

});

Getter in ViewModel:

public LiveData<List<?>> getList() {
    return getDataList();
}

Code in Adapter:

public class AppAdapter extends RecyclerView.Adapter<AppAdapter.AppViewHolder> {

...

public void setList(ArrayList<MyObj> list) {
    mAppList = list;
    notifyDataSetChanged();
}

Thanks in advance.

CodePudding user response:

mDataList is null because the call you're making to get data is asynchronous. This means that the function will return before your api call (and its callbacks) does.

Read more here https://www.bmc.com/blogs/asynchronous-programming/

CodePudding user response:

To get the list you need to observe data changes.

So something like this:

From the activity:

Kotlin:

viewModel.getDataList().observe(this) { dataList ->
  //TODO: Do something with the list
}

Java:

final Observer<String> listObserver = new Observer<List<>>() {
   @Override
   public void onChanged(@Nullable final List<> newName) {
      //TODO: Use the list
   }
};

viewModel.getDataList().observe(this, listObserver);

The logic inside the observer will be called every time the list changes.

CodePudding user response:

I solved it this way:

  1. Changed this line in Fragment's onCreate method

from:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

pageViewModel = new  ViewModelProvider(this).get(PageViewModel.class);

to

pageViewModel = new ViewModelProvider(requireActivity()).get(PageViewModel.class);

Thanks to this answer

  1. Changed the data type of getDataList() method in the PageViewModel class from LiveData to MutableLiveData (without it the above step doesn't help):

from:

public class PageViewModel extends ViewModel {  
    private MutableLiveData<List<?>> mDataList;
    ...


    private LiveData<List<?> getDataList() {

to:

    private MutableLiveData<List<?> getDataList() {

and now it works! The observer gets triggered now and the data are updated.

  • Related