Search code examples
javaandroidandroid-architecture-componentsandroid-livedataandroid-viewmodel

LiveData doesn't show data initially


I'm fetching some data from my server and observing it using LiveData. When my fragment starts initially, it doesn't return anything in onChanged(), and after switching fragment, it triggers onChanged() with right data.

After debugging, I saw that my API call is successful and I get data there, but it is not returning data through onChanged() initially.

Repository:

public class UserRepository {
    private ApiService apiService;
    private static UserRepository repository;
    private MutableLiveData<User> user = new MutableLiveData<>();

    private UserRepository() {
        apiService = RestClient.getClient().create(ApiService.class);
    }

    public synchronized static UserRepository getInstance() {
        if (repository == null) repository = new UserRepository();
        return repository;
    }

    public LiveData<User> getUser() {
        return user;
    }

    public void fetchUser() {
        Call<User> call = apiService.getUser();
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
                if (response.body() != null) user.postValue(response.body());
            }

            @Override
            public void onFailure(@NonNull Call<User> call, @NonNull Throwable t) {
                user.postValue(null);
                t.printStackTrace();
            }
        });
    }
}

ViewModel:

public class UserViewModel extends AndroidViewModel {
    private LiveData<User> user;

    public UserViewModel(@NonNull Application application) {
        super(application);
        user = UserRepository.getInstance().getUser();
    }

    public LiveData<User> getUser() {
        return user;
    }
}

Fragment:

userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
userViewModel.getUser.observe(this, user -> {
    //Set data
})

I can solve this problem by calling UserRepository.getInstance().getUser() before observing in my fragment. But that is not the preferred method as said by Google, and kind of a workaround.


Solution

  • As already mentioned in the comments, you seem to call getUser() on the repository before you even set any value in fetchUser(). Change fetchUser() so that it returns LiveData and then call that method from the view model instead of getUser():

    public LiveData<User> fetchUser() {
        Call<User> call = apiService.getUser();
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
                if (response.body() != null) user.postValue(response.body());
            }
    
            @Override
            public void onFailure(@NonNull Call<User> call, @NonNull Throwable t) {
                user.postValue(null);
                t.printStackTrace();
            }
        });
    
        return user;
     }
    

    And then in the view model:

    public UserViewModel(@NonNull Application application) {
        super(application);
        user = UserRepository.getInstance().fetchUser();
    }