javaretrofitwebapienqueue

How to wait for webApi response with retrofit enqueue?


I use the following function to check the username and password from the web service. I want to use this function in different activities. But before I get a response from the server, the function reaches the end and returns false. Please suggest me a way to solve this issue with sample code.

public boolean checkUserPass(String username, String password) {
    userExist = false;
    UserModel user = new UserModel();
    user.setUsername(username);
    user.setPassword(password);
    Assistant.assistant.client.userCheck(user).enqueue(new Callback<Boolean>() {
        @Override
        public void onResponse(@NonNull Call<Boolean> call, @NonNull Response<Boolean> response) {
            if (response.isSuccessful()) {
                userExist = response.body();
                Log.i("MyTag", response.body().toString());
            } else {
                Log.i("MyTag", "Not Success " + response);
            }
        }

        @Override
        public void onFailure(Call call, Throwable throwable) {
            Log.e("MyTag", "Fail");
        }
    });
    return userExist;
}

I searched all the posts but didn't get a proper answer.


Solution

  • The issue you're encountering is due to the asynchronous nature of the enqueue method in Retrofit. The enqueue method executes the network request on a background thread and immediately returns, while your function checkUserPass continues execution and returns false before the network request completes.

    Here are the two solutions to resolve this and choose one of your needs:

    1. Using a Callback Interface Define a callback interface to handle the result of the network request and invoke the callback in the Retrofit response methods.

    public interface UserCheckCallback {
        void onResult(boolean userExists);
        void onError(Throwable throwable);
    }
    
    public void checkUserPass(String username, String password, UserCheckCallback callback) {
        UserModel user = new UserModel();
        user.setUsername(username);
        user.setPassword(password);
        Assistant.assistant.client.userCheck(user).enqueue(new Callback<Boolean>() {
            @Override
            public void onResponse(@NonNull Call<Boolean> call, @NonNull Response<Boolean> response) {
                if (response.isSuccessful()) {
                    boolean userExist = response.body();
                    Log.i("MyTag", response.body().toString());
                    callback.onResult(userExist);
                } else {
                    Log.i("MyTag", "Not Success " + response);
                    callback.onResult(false);
                }
            }
    
            @Override
            public void onFailure(Call<Boolean> call, Throwable throwable) {
                Log.e("MyTag", "Fail", throwable);
                callback.onError(throwable);
            }
        });
    }
    

    You can then call this function and handle the result in the callback:

    checkUserPass("username", "password", new UserCheckCallback() {
        @Override
        public void onResult(boolean userExists) {
            // Handle the result here
            if (userExists) {
                // User exists
            } else {
                // User does not exist
            }
        }
    
        @Override
        public void onError(Throwable throwable) {
            // Handle the error here
        }
    });
    

    2. Using Kotlin Coroutines (Recommended for Modern Android Development) If you are using Kotlin, you can leverage coroutines to handle asynchronous tasks more easily.

    suspend fun checkUserPass(username: String, password: String): Boolean {
        val user = UserModel(username, password)
        return try {
            val response = Assistant.assistant.client.userCheck(user).await()
            if (response.isSuccessful) {
                response.body() ?: false
            } else {
                Log.i("MyTag", "Not Success $response")
                false
            }
        } catch (e: Exception) {
            Log.e("MyTag", "Fail", e)
            false
        }
    }
    

    You can call this function from a coroutine scope:

    CoroutineScope(Dispatchers.IO).launch {
        val userExists = checkUserPass("username", "password")
        withContext(Dispatchers.Main) {
            if (userExists) {
                // User exists
            } else {
                // User does not exist
            }
        }
    }