Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I return value from onResponse of Retrofit v2

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 ?
}
like image 913
Yunus Haznedar Avatar asked Jul 02 '17 14:07

Yunus Haznedar


1 Answers

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);
like image 61
Fred Avatar answered Sep 22 '22 10:09

Fred