javaandroidjsonasynchronousokhttp

How to get async call to return response to main thread, using okhttp?


I'm trying to make a program for Android, and I'm using okhttp for json-calls. I really want to return my response to the outside of the thread I'm creating. I need to create the thread for an async call otherwise I'll get a NetworkOnMainThreadException. Problem is that I can't seem to get my response string outside the "onResponse" method, even though my responseString is a global variable in the class. Since it's async, the thread won't run in time to get my value in the global variable before it returns. How can I make sure I get the response, before it returns my responseString value?

Here's my code:

public static String getUserProductCategoriesFromServer(Activity activity, final String UID, final String EXPIRY, final String CLIENT, final String ACCESSTOKEN)
{
    activity.runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            final OkHttpClient client = new OkHttpClient();
            final Request request = new Request.Builder()
                    .url(JsonStorage.getJsonUserProductCategories())
                    .get()
                    .addHeader("access-token", ACCESSTOKEN)
                    .addHeader("client", CLIENT)
                    .addHeader("expiry", EXPIRY)
                    .addHeader("uid", UID)
                    .build();



            client.newCall(request).enqueue(new Callback()
            {
                @Override
                public void onFailure(Call call, IOException e)
                {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException
                {
                    try
                    {
                        response = client.newCall(request).execute();
                        String json = response.body().string();
                        JSONObject jsonObject = new JSONObject(json);
                        JSONArray jsonData = (JSONArray) jsonObject.getJSONArray("user_product_category_names");
                        responseString = jsonData.toString();
                        Log.v("TEST1", jsonData.toString()); //RETURNS JSON :D
                        Log.v("TEST2", responseString); //RETURNS JSON :D
                    } catch (IOException | JSONException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    });
    Log.v("TEST3", responseString); //RETURNS "NULL" :(
    return responseString;
}

Solution

  • Common way to get results from asynchronous world to synchronous (thread-based) is to use Futures. Many libraries implement such an interface, e.g. Guava. Standard java has implementation named CompletableFuture. It uses methods with different names and signatures, but an adapter can be easily implemented:

    class CallbackFuture extends CompletableFuture<Response> implements Callback {
        public void onResponse(Call call, Response response) {
             super.complete(response);
        }
        public void onFailure(Call call, IOException e){
             super.completeExceptionally(e);
        }
    }
    

    Then you can use it as follows:

    CallbackFuture future = new CallbackFuture();
    client.newCall(request).enqueue(future);
    Response response = future.get();
    

    Having the Response, you can extract responseString the same way as you did it in the your first variant.