How to Return String or JSONobject from Asynchronous Callback Using Retrofit

How can I return String or JSONObject from asynchronous callback using Retrofit?

I figured it out. It's embarrassing but it was very simple... Temporary solution may be like this:

 public void success(Response response, Response ignored) {
TypedInput body = response.getBody();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(body.in()));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}

// Prints the correct String representation of body.
System.out.println(out);
} catch (IOException e) {
e.printStackTrace();
}
}

But if you want to get directly Callback the Better way is to use Converter.

public class Main {
public interface ApiService {
@GET("/api/")
public void getJson(Callback<String> callback);
}

public static void main(String[] args) {
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new MockClient())
.setConverter(new StringConverter())
.setEndpoint("http://www.example.com").build();

ApiService service = restAdapter.create(ApiService.class);
service.getJson(new Callback<String>() {
@Override
public void success(String str, Response ignored) {
// Prints the correct String representation of body.
System.out.println(str);
}

@Override
public void failure(RetrofitError retrofitError) {
System.out.println("Failure, retrofitError" + retrofitError);
}
});
}

static class StringConverter implements Converter {

@Override
public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
String text = null;
try {
text = fromStream(typedInput.in());
} catch (IOException ignored) {/*NOP*/ }

return text;
}

@Override
public TypedOutput toBody(Object o) {
return null;
}

public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}

public static class MockClient implements Client {
@Override
public Response execute(Request request) throws IOException {
URI uri = URI.create(request.getUrl());
String responseString = "";

if (uri.getPath().equals("/api/")) {
responseString = "{result:\"ok\"}";
} else {
responseString = "{result:\"error\"}";
}

return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST,
new TypedByteArray("application/json", responseString.getBytes()));
}
}
}

If you know how to improve this code - please feel free to write about it.

How can I return value from onResponse of Retrofit v2

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);

Retrofit. Android. Can I download json object to String without parsing?

This happens because the response is a JSON Object and not a String. You could do the following:
First change the getter method:

@GET("/api.php?action=bundle")
public void getWholeScheduleString(Callback<JsonObject> response);

Then in the success(JsonObject json, Response res) method of your Callback you just map it to a String:

@Override
public void success(JsonObject response, Response arg1) {
String myResponse = response.getAsString();
}

EDIT: By the way, you can parameterize the query part (?action=bundle) to make the method more generic like this:

@GET("/api.php")
public void getWhateverAction(@Query("action") String action, Callback<JsonObject> response);

You pass the 'action' as a String argument whenever you call the method.

Retrofit callback get response body

Inside callback's angle brackets write "Response" and then extract the stream from this response.

service.getToto("toto", new Callback<Response>() {
@Override
public void success(Response result, Response response) {

//Try to get response body
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
try {

reader = new BufferedReader(new InputStreamReader(result.getBody().in()));

String line;

try {
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}

String result = sb.toString();
}

@Override
public void failure(RetrofitError error) {

}
});

How can I return value from function onResponse of Retrofit?

The problem is you are trying to synchronously return the value of enqueue, but it is an asynchronous method using a callback so you can't do that. You have 2 options:

  1. You can change your RequestGR method to accept a callback and then chain the enqueue callback to it. This is similar to mapping in frameworks like rxJava.

This would look roughly like:

public void RequestGR(LatLng start, LatLng end, final Callback<JSONArray> arrayCallback)
{

EndpointInterface loginService = ServiceAuthGenerator.createService(EndpointInterface.class);
Call<GR> call = loginService.getroutedriver();
call.enqueue(new Callback<GR>() {
@Override
public void onResponse(Response<GR> response , Retrofit retrofit)
{

JSONArray jsonArray_GR = response.body().getRoutes();
arrayCallback.onResponse(jsonArray_GR);
}
@Override
public void onFailure(Throwable t) {
// error handling? arrayCallback.onFailure(t)?
}
});
}

The caveat with this approach is it just pushes the async stuff up another level, which might be a problem for you.


  1. You can use an object similar to a BlockingQueue, Promise or an Observable or even your own container object (be careful to be thread safe) that allows you to check and set the value.

This would look like:

public BlockingQueue<JSONArray> RequestGR(LatLng start, LatLng end)
{
// You can create a final container object outside of your callback and then pass in your value to it from inside the callback.
final BlockingQueue<JSONArray> blockingQueue = new ArrayBlockingQueue<>(1);
EndpointInterface loginService = ServiceAuthGenerator.createService(EndpointInterface.class);
Call<GR> call = loginService.getroutedriver();
call.enqueue(new Callback<GR>() {
@Override
public void onResponse(Response<GR> response , Retrofit retrofit)
{

JSONArray jsonArray_GR = response.body().getRoutes();
blockingQueue.add(jsonArray_GR);
}
@Override
public void onFailure(Throwable t) {
}
});
return blockingQueue;
}

You can then synchronously wait for your result in your calling method like this:

BlockingQueue<JSONArray> result = RequestGR(42,42);
JSONArray value = result.take(); // this will block your thread

I would highly suggest reading up on a framework like rxJava though.

How to handle Dynamic JSON in Retrofit?

Late to the party, but you can use a converter.

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://graph.facebook.com")
.setConverter(new DynamicJsonConverter()) // set your static class as converter here
.build();

api = restAdapter.create(FacebookApi.class);

Then you use a static class which implements retrofit's Converter:

static class DynamicJsonConverter implements Converter {

@Override public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
try {
InputStream in = typedInput.in(); // convert the typedInput to String
String string = fromStream(in);
in.close(); // we are responsible to close the InputStream after use

if (String.class.equals(type)) {
return string;
} else {
return new Gson().fromJson(string, type); // convert to the supplied type, typically Object, JsonObject or Map<String, Object>
}
} catch (Exception e) { // a lot may happen here, whatever happens
throw new ConversionException(e); // wrap it into ConversionException so retrofit can process it
}
}

@Override public TypedOutput toBody(Object object) { // not required
return null;
}

private static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append("\r\n");
}
return out.toString();
}
}

I have written this sample converter so it returns the Json response either as String, Object, JsonObject or Map< String, Object >. Obviously not all return types will work for every Json, and there is sure room for improvement. But it demonstrates how to use a Converter to convert almost any response to dynamic Json.



Related Topics



Leave a reply



Submit