How can I return value from onResponse of Retrofit v2

15,880

Solution 1

onResponse is asynchronous so it will most probably finish after revealCourtPlace has returned.

You cannot return anything from inside onResponse like that. You can however, pass the value up or restructure your code to work with something like Rx.

Let me explain. So, one option is to pass the string value you want up with a callback. Say you have the interface:

public interface RevealCourtPlaceCallbacks {
     void onSuccess(@NonNull String value);

     void onError(@NonNull Throwable throwable);
}

These are the methods that whoever wants to receive the value of your network call will have to implement. You use this for example by passing it to the method revealCourtPlace

public void revealCourtPlace(String courtID, @Nullable RevealCourtPlaceCallbacks callbacks)
{  
   BaseService.getInstance()
      .getUniqueCourt(Session.getToken(),courtID)
      .enqueue(new Callback<JsonObject>() {
    @Override
    public void onResponse(Call<JsonObject> call, Response<JsonObject> response)
    {

        JsonObject object = response.body();
        boolean success = object.get("success").getAsBoolean(); //json objesinde dönen success alanı true ise
        if (success)
        {
            JsonArray resultArray = object.get("data").getAsJsonObject().get("result").getAsJsonArray();
            for (int i = 0; i < resultArray.size(); i++)
            {
                JsonObject jsonInfoResult = resultArray.get(i).getAsJsonObject();
                String courtName=jsonInfoResult.get("name").getAsString();

                if (callbacks != null)
                  calbacks.onSuccess(courtName);
            }

        }

    }

    @Override
    public void onFailure(Call<JsonObject> call, Throwable t)
    {
        if (callbacks != null)
            callbacks.onError(t);
    }

  });
}

Important things to notice: The method returns void. You pass the callbacks as an argument. These callbacks must be implemented by who's calling the method, or as an anonymous class implemented on the calling spot.

This enables you to receive the string courtName asynchronously and not having to worry about returning a value.

There's another option where you could make your code reactive. This is a bit more work and a shift in paradigm. It also requires knowledge in Rx java. I'll leave here an example of how this can be done. Bear in mind that there are several ways of doing this.

First you should define the retrofit interface differently. The return type must now be an observable:

public interface CourtApiClient {
    @GET(/*...*/)
    Single<JsonObject> getUniqueCourt(/*...*/);
}

I don't really know the entire interface details of your call, but the important part here is the return type is now Single. This is an Rx observable that emits only one item or errors. The type should also be something else than JsonObject, but this is quite hard to tell from your code what should it be. Anyway, this will work too.

The next step is to simply return the result from your revealCourtPlace method:

public Single<JsonObject> revealCourtPlace(String courtID, @Nullable RevealCourtPlaceCallbacks callbacks)
{  
   return BaseService.getInstance()
      .getUniqueCourt(Session.getToken(),courtID);
}

The key difference here is that the method now returns the observable and you can subscribe to it whenever you want. This makes the flow seem synchronous although it's in fact asynchronous.

You have now the choice to either map the JsonObject to the several strings you want, or to do the parsing in your subscriber.

Edit

Since you asked in the comments how you can call your function here's a possibility:

revealCourtPlace("some court id", new RevealCourtPlaceCallbacks() {
       @Override
       public void onSuccess(@NonNull String value) {
          // here you use the string value
       }

       @Override
       public void onError(@NonNull Throwable throwable) {
          // here you access the throwable and check what to do
       }
  });

Alternatively you can make the calling class implement these callbacks and simply pass this:

revealCourtPlace("some court id", this);

Solution 2

With retrofit2 is possible make synchronous call:

Callback<JsonObject> callback = BaseService.getInstance().getUniqueCourt(Session.getToken(),courtID)
Response<JsonObject> response = callback.execute();
...

however, synchronous requests trigger app crashes on Android 4.0 or newer. You’ll run into the NetworkOnMainThreadException error.

More information here.

Share:
15,880
Yunus Haznedar
Author by

Yunus Haznedar

Updated on June 16, 2022

Comments

  • Yunus Haznedar
    Yunus Haznedar about 2 years

    I want to return a string value from my function. But I do not know how to handle it? I tried final one-array solution but it did not work out.

    Here is my code:

    public String revealCourtPlace(String courtID)
    {
    
        BaseService.getInstance().getUniqueCourt(Session.getToken(),courtID).enqueue(new Callback<JsonObject>()
        {
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response)
            {
    
                JsonObject object = response.body();
                boolean success = object.get("success").getAsBoolean(); //json objesinde dönen success alanı true ise
                if (success)
                {
                    JsonArray resultArray = object.get("data").getAsJsonObject().get("result").getAsJsonArray();
                    for (int i = 0; i < resultArray.size(); i++)
                    {
                        JsonObject jsonInfoResult = resultArray.get(i).getAsJsonObject();
                        String courtName=jsonInfoResult.get("name").getAsString();
                    }
    
                }
    
            }
    
            @Override
            public void onFailure(Call<JsonObject> call, Throwable t)
            {
    
            }
    
        });
    
        //return ?
    }
    
  • Yunus Haznedar
    Yunus Haznedar almost 7 years
    Thank you for guiding reply sir. It is just so amazing. But how can call my function? revealCourtName(courtID,?);
  • Fred
    Fred almost 7 years
    @YunusHaznedar I've edited my answer. Check out if it helps you out!
  • Yunus Haznedar
    Yunus Haznedar almost 7 years
    Thank you for your guiding reply sir. It will help me out to see what I'm doing wrong. Thanks again.