1

I'm trying to make an async call using retrofit, but it is returning null. I was previously able to get a synchronous call to work just fine, but my async version is not working.

Here is the code where I make the call:

// Get the nodes from Heroku
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(
        "http://myURL.com").build();
HerokuService herokuService = restAdapter.create(HerokuService.class);
Callback<Node[]> callback = new Callback<Node[]>()
{
    @Override
    public void failure(RetrofitError err)
    {
    }

    @Override
    public void success(Node[] nodeArray, Response response)
    {
        tempNodes = nodeArray;
    }
};
herokuService.nodes(callback);
// tempNodes is null here

Here is the contents of HerokuService.java

public interface HerokuService
{
    @GET("/nodes")
    void nodes(Callback<Node[]> callback);
}

I have already checked that there is internet connection, so I'm sure that is not the issue.

I also tried setting tempNodes to a dummy value inside failure but it was still null. This to me indicates that it is reaching success but that it is being set to null there.

Any thoughts on why this is happening or what I might try to fix it? Thanks!

EDIT: I tried blocking in case I wasn't waiting long enough for the callback to return as follows:

CountDownLatch latch = new CountDownLatch(1); //Class variable

//...

Callback<Node[]> callback = new Callback<Node[]>()
{
    @Override
    public void failure(RetrofitError err)
    {
        latch.countDown();
    }

    @Override
    public void success(Node[] nodeArray, Response response)
    {
        tempNodes = nodeArray;
        latch.countDown();
    }
 };
herokuService.nodes(callback);
try
{
    latch.await();
}
catch (InterruptedException e)
{
    e.printStackTrace();
}

But now it just hangs and never gets past latch.await(). Seems like the callback is just never returning?

Akshay
  • 814
  • 6
  • 19

2 Answers2

1

At which point are you evaluating tempNodes? Since this is an asynchronous operation you have to wait until the callback is actually called. If you're waiting for the result anyway, leave out the callback and set the return value of nodes() to Node[] or List<Node>.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • I'm evaluating where I left the comment indicating so. But I'm guessing you're right that the callback hasn't returned by then. Is there a way that I can block until it returns? – Akshay Jul 13 '14 at 14:25
  • And also, I did already try a synchronous call as you suggested and it worked fine. I'm trying to use an async call specifically here. – Akshay Jul 13 '14 at 14:26
  • Use a semaphore or some other synchronization mechanism. To be honest, this would defeat the purpose of asynchronous callbacks, though. – Kirill Rakhman Jul 13 '14 at 14:34
  • Yes, I do realize that. For now I'm just trying to implement this asynchronously for the sake of doing so :) – Akshay Jul 13 '14 at 14:36
  • Hold on a minute. This is still not working. Now the callback is just never returning and it's blocking forever. Any idea why the callback might not be returning? – Akshay Jul 13 '14 at 14:45
  • I can't comment on that without looking at your implementation. – Kirill Rakhman Jul 13 '14 at 14:55
  • Ok, I've added the blocking implementation. – Akshay Jul 13 '14 at 15:03
  • Does your app have the Internet permission? Do the logs say anything interesting? Set the logging level of the Retrofit adapter to max and try to grab something interesting from it. – Kirill Rakhman Jul 13 '14 at 15:16
  • I definitely have internet permissions etc. As I already stated, this was working perfectly for a synchronous call. I'll keep checking the logs and see if I can figure this out. – Akshay Jul 13 '14 at 15:21
1

Figured it out. Seems like I'm not waiting for the callback to return before accessing tempNodes. I realized that I can block using a CountDownLatch as described here: https://stackoverflow.com/a/7735374/3399526 and then proceed after the callback finishes.

I had to use the sync version, and then run it on a seperate thread. This is the final working version:

//Get the nodes from heroku, and block until we get them
final CountDownLatch latch = new CountDownLatch(1);
Thread thread = new Thread(new Runnable()
{
    @Override
    public void run()
    {
        try
        {
            // Get the nodes from Heroku
            RestAdapter restAdapter = new RestAdapter.Builder()
                    .setEndpoint("http://safe-hollows-9286.herokuapp.com")
                    .build();
            HerokuService herokuService = restAdapter.create(HerokuService.class);
            tempNodes = herokuService.nodes();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        latch.countDown();
    }
});
thread.start();
try
{
    latch.await();
}
catch (InterruptedException e)
{
    e.printStackTrace();
}
Community
  • 1
  • 1
Akshay
  • 814
  • 6
  • 19