Search code examples
androidandroid-architecture-componentsandroid-livedata

How to connect ViewModel with Repository so that data is propagated to the View (MVVM, Livedata)


I've added some code to make my question more clear.

Retrofit interface:

public interface JsonPlaceHolderAPI {
    public static final String BASE_URL = "https://jsonplaceholder.typicode.com/";

    @GET("todos/{number}")
    Call<ResponseBody> getJsonResponse(@Path("number") String number);
}

The repository: --> fetchResponse() takes Viewmodel's MutableLiveData as parameter and uses it to update its value and then trigger View to change its UI.

public class Repository {

    private final JsonPlaceHolderAPI api;

    public Repository() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .build();
        api = retrofit.create(JsonPlaceHolderAPI.class);
    }


    public void fetchResponse(String number, final MutableLiveData<CharSequence> mld){
        final MutableLiveData<CharSequence> ml = new MutableLiveData<>();

        api.getJsonResponse(number).enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                try {
                    mld.setValue(response.body().string());

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {}
        });
    }
}

The viewModel:

public class MainActivityViewModel extends AndroidViewModel {
    MutableLiveData<CharSequence> response = new MutableLiveData<>();
    Repository repository;

    public MainActivityViewModel(@NonNull Application application) {
        super(application);
        repository = new Repository();
    }


    public void fetchData(String number) {
        response.setValue("Loading data");
        repository.fetchResponse(number, response);
    }

    public LiveData<? extends CharSequence> getLiveData() {
        return response;
    }
}

The View:

...
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
        initViews();

        viewModel.getLiveData().observe(this, new Observer<CharSequence>() {
            @Override
            public void onChanged(CharSequence charSequence) {
                if (charSequence != null) {
                    txt.setText(charSequence);
                }
            }
        });


    }
    ...

I am not sure if I should pass the MutableLiveData from the viewModel to the Repository.

Is there any recommended way to let viewModel know that data is ready to be published from Repository??

I have read a lot of questions and articles and still I don't get it. I would love if somebody explain to me a nice way to achieve it!


Solution

  • Api

    public interface TodoApi {
        @GET("todos/")
        Call<List<Todo>> getTodos();
    
        @GET("todos/{id}")
        Call<Todo> getTodo(@Path("id") long id);
    }
    

    Respository

        public class TodoRepository {
        private static final String TAG = "TodoRepository";
        private static final TodoRepository ourInstance = new TodoRepository();
        private TodoApi api;
    
        private MutableLiveData<List<Todo>> todoListLiveData = new MutableLiveData<>();
        private MutableLiveData<Todo> todoLiveData = new MutableLiveData<>();
    
        public static TodoRepository getInstance() {
            return ourInstance;
        }
    
        private TodoRepository() {
            api = ApiBuilder.create(TodoApi.class);
        }
    
        public LiveData<List<Todo>> getTodos() {
            api.getTodos().enqueue(new Callback<List<Todo>>() {
                @Override
                public void onResponse(@NonNull Call<List<Todo>> call, @NonNull Response<List<Todo>> response) {
                    todoListLiveData.setValue(response.body());
                }
    
                @Override
                public void onFailure(@NonNull Call<List<Todo>> call, @NonNull Throwable t) {
                    Log.d(TAG, "onFailure: failed to fetch todo list from server");
                }
            });
            return todoListLiveData;
        }
    
        public LiveData<Todo> getTodo(long id) {
            api.getTodo(id).enqueue(new Callback<Todo>() {
                @Override
                public void onResponse(@NonNull Call<Todo> call, @NonNull Response<Todo> response) {
                    todoLiveData.setValue(response.body());
                }
    
                @Override
                public void onFailure(@NonNull Call<Todo> call, @NonNull Throwable t) {
                    Log.d(TAG, "onFailure: failed to get todo");
                }
            });
            return todoLiveData;
        }
    }
    

    ViewModel

        public class MainActivityViewModel extends ViewModel {
        private static final String TAG = "MainActivityViewModel";
    
        private TodoRepository repository = TodoRepository.getInstance();
    
        private MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
        private LiveData<List<Todo>> todoLiveData;
    
        public MainActivityViewModel() {
            super();
            isLoading.setValue(true);
            todoLiveData = repository.getTodos();
        }
    
        @Override
        protected void onCleared() {
            super.onCleared();
        }
    
        public MutableLiveData<Boolean> getIsLoading() {
            return isLoading;
        }
    
        public LiveData<List<Todo>> getTodoLiveData() {
            return todoLiveData;
        }
    }
    

    View

    @Override

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        todoListRecyclerView = findViewById(R.id.todo_recycler_view);
        loadingIndicator = findViewById(R.id.todo_loading_indicator);
        mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
        getSupportActionBar().setTitle("Todos");
    
        mViewModel.getIsLoading().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean isLoading) {
                if (isLoading) loadingIndicator.setVisibility(View.VISIBLE);
                else loadingIndicator.setVisibility(View.GONE);
            }
        });
    
        mViewModel.getTodoLiveData().observe(this, new Observer<List<Todo>>() {
            @Override
            public void onChanged(List<Todo> todos) {
                mViewModel.getIsLoading().postValue(false);
                initRecyclerView(todos);
            }
        });
    }
    

    For full sample

    https://github.com/AnvarNazar/RetrofitTypicodeApiExample