Is this the right way to have a button that retries an API call on an Android app?

Bricktop :

The project I'm working on gets a list of users from an API without Room in the middle (network only architecture). I actually have the code working the way I want it to work, but I'm wondering if this is the right way to do it. Below I'm marking where exactly I'm having doubts.

My UI is a RecyclerView backed by a ListAdapter. I have a ViewModel (mainViewModel) that is observing the list of users provided by the Repository class and also a boolean to show, or not, the "Try Again" button.

In MainActivity.java I have

// Observing the boolean that represents if the api call failed
mainViewModel.getCallFailure().observe(this, new Observer<Boolean>() {
    @Override
    public void onChanged(Boolean aBoolean) {
        if (aBoolean) {
            tryAgainButton.setVisibility(View.VISIBLE);
        } else {
            tryAgainButton.setVisibility(View.GONE);
        }
    }
});

// Setting up onClickListener
tryAgainButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mainViewModel.retryCall(); // HERE IS WHERE I'M HAVING DOUBTS
    }
});

The MainViewModel.java has

public class MainViewModel extends ViewModel {

    ...

    public LiveData<Boolean> getCallFailure(){
        return userRepo.getCallFailure();
    }

    // THIS IS THE METHOD I'M CALLING FROM THE UI
    public void retryCall(){
        userRepo.getRetryCallback();
    }
}

And finally, the Repository.java is

public class Repository {
    private Call<UsersResponse> retryCall;
    private Callback<UsersResponse> callback;
    private final MutableLiveData<Boolean> callFailure = new MutableLiveData<>();

    public LiveData<List<UserModel>> getListUsers(){
        final MutableLiveData<List<UserModel>> users = new MutableLiveData<>();
        callFailure.setValue(false);

        (Api.getClient().getUsersList()).enqueue(new Callback<UsersResponse>() {
            @Override
            public void onResponse(Call<UsersResponse> call, Response<UsersResponse> response) {
                users.setValue(response.body().getUsersList());
                callFailure.setValue(false);
            }

            @Override
            public void onFailure(Call<UsersResponse> call, Throwable t) {
                Log.d("Response GET", t.toString());
                callFailure.setValue(true);
                setCallback(this);
                retryCall = call.clone();
            }
        });

        return users;
    }

    private void setCallback(Callback<UsersResponse> usersResponseCallback) {
        callback = usersResponseCallback;
    }

    public LiveData<Boolean> getCallFailure(){
        return callFailure;
    }

    public void getRetryCallback(){
        retryCall.enqueue(callback);
    }
}

I tried to mention just the code that I think is relevant, I manage a ProgressDialog the same way I'm doing with the boolean callFailure in the Repository and it's working wonders. But honestly, something feels off having the mainViewModel.retryCall(); call in the MainActivity.java file, I'd really appreciate any suggestions!

Romain Goutte-Fangeas :

In the projects I made using MVVM my activities and fragments notify my ViewModel like you expose (by calling a method which act like a trigger) and observe the result through LiveData.

One thing that's strange for me is that your repository store a part of the state (the retry and failure). In my projects I put this logic in the ViewModel and my repositories are here to do simple operations (like a network call). But this is more a personal choice than anything else.


EDIT: here is how I would have done this based on your code (maybe a better way exists).

Basically, I prefer to manage the LiveData objects in the ViewModel than in the Repository, because in my mind the repository layer is just a bridge between the application and the data (in the net or in a DB), and the state of the UI (like the retry button visibility) must be managed by the ViewModel.

Repository.java:

public class Repository {
    public void getListUsers(Callback<UserResponse> callback) {
        (Api.getClient().getUsersList()).enqueue(callback);
    }
}

MainViewModel.java:

public class MainViewModel extends ViewModel {

    ...

    private final MutableLiveData<Boolean> callFailure = new MutableLiveData<>();
    private final MutableLiveData<List<UserModel>> users = new MutableLiveData<>();

    public LiveData<Boolean> getCallFailure() {
        return callFailure;
    }

    public LiveData<Boolean> getUsers() {
        return users;
    }

    // THIS IS THE METHOD I'M CALLING FROM THE UI
    public void retryGetUsers() {
        callFailure.setValue(false);
        getUsers();
    }

    private void getUsers() {
        userRepo.getListUsers(new Callback<UserResponse>(){
            @Override
            public void onResponse(Call<UsersResponse> call, Response<UsersResponse> response) {
                users.setValue(response.body().getUserList());
            }

            @Override
            public void onFailure(Call<UsersResponse> call, Throwable t) {
                callFailure.setValue(true);
            }
        });
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=166525&siteId=1