1

When trying to access a RESTful api from an android app, you want to check two things:

  1. That the User's wifi is enabled (and there's a established connection)
  2. Your app can actually reach the server. (my server is on a local network)

I've managed to check for the first one, but I have no idea how to check the latest.

Here's my output

05-25 15:49:46.929  15485-15999/com.tesis.restapp.restapp E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
    java.lang.RuntimeException: An error occured while executing doInBackground()
            at android.os.AsyncTask$3.done(AsyncTask.java:278)
            at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
            at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:856)
     Caused by: retrofit.RetrofitError: java.net.ConnectException: failed to connect to /192.168.1.25 (port 8080) after 15000ms: isConnected failed: ECONNREFUSED (Connection refused)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:385)
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:241)
            at $Proxy0.logIn(Native Method)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:83)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:70)
            at android.os.AsyncTask$2.call(AsyncTask.java:264)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:856)
     Caused by: java.net.ConnectException: failed to connect to /192.168.1.25 (port 8080) after 15000ms: isConnected failed: ECONNREFUSED (Connection refused)
            at libcore.io.IoBridge.isConnected(IoBridge.java:220)
            at libcore.io.IoBridge.connectErrno(IoBridge.java:158)
            at libcore.io.IoBridge.connect(IoBridge.java:118)
            at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
            at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
            at java.net.Socket.connect(Socket.java:849)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:77)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:50)
            at libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:351)
            at libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:86)
            at libcore.net.http.HttpConnection.connect(HttpConnection.java:128)
            at libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:308)
            at libcore.net.http.HttpEngine.connect(HttpEngine.java:303)
            at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
            at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
            at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
            at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:188)
            at retrofit.client.UrlConnectionClient.prepareRequest(UrlConnectionClient.java:66)
            at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:322)
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:241)
            at $Proxy0.logIn(Native Method)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:83)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:70)
            at android.os.AsyncTask$2.call(AsyncTask.java:264)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:856)
     Caused by: libcore.io.ErrnoException: isConnected failed: ECONNREFUSED (Connection refused)
            at libcore.io.IoBridge.isConnected(IoBridge.java:207)
            at libcore.io.IoBridge.connectErrno(IoBridge.java:158)
            at libcore.io.IoBridge.connect(IoBridge.java:118)
            at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
            at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
            at java.net.Socket.connect(Socket.java:849)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:77)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:50)
            at libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:351)
            at libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:86)
            at libcore.net.http.HttpConnection.connect(HttpConnection.java:128)
            at libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:308)
            at libcore.net.http.HttpEngine.connect(HttpEngine.java:303)
            at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
            at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
            at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
            at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:188)
            at retrofit.client.UrlConnectionClient.prepareRequest(UrlConnectionClient.java:66)
            at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:322)
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:241)
            at $Proxy0.logIn(Native Method)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:83)
            at com.tesis.restapp.restapp.activities.intro.IntroActivity$SignIn.doInBackground(IntroActivity.java:70)
            at android.os.AsyncTask$2.call(AsyncTask.java:264)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:856)

EDIT

Here's my interface (for now, it just tries to log a user in)

public interface RestAppApiInterface {

    @FormUrlEncoded
    @POST(Constants.URL_LOGIN)
    public User logIn(@Field("username") String username, @Field("password") String password);

}

This class returns an instance of the interface:

public class ApiClient {

    private static final String API_URL = "http://192.168.1.25:8080";

    private static RestAppApiInterface sRestAppService;

    public static RestAppApiInterface getRestAppApiClient() {
        if (sRestAppService == null) {
            RestAdapter restAdapter = new RestAdapter.Builder()
                    .setEndpoint(API_URL)
                    .build();
            sRestAppService = restAdapter.create(RestAppApiInterface.class);
        }
        return sRestAppService;
    }

}

I call the service within a AsynTask like this

RestAppApiInterface apiInterface = ApiClient.getRestAppApiClient();
apiInterface.logIn("username","p@55w0rd");
frankelot
  • 13,666
  • 16
  • 54
  • 89
  • 1
    Can you share how you configured your RestAdapter? – Alécio Carvalho May 25 '14 at 19:11
  • It all seems OK, to eliminate any other external problem. Download a Rest Client extension for your Firefox or Chrome and make a post request with the same parameters there..to see if everything is working as expected. It might also be that your firewall is blocking this port. So you wanna eliminate that first. – Alécio Carvalho May 25 '14 at 19:28
  • Sorry, I might not have been clear enough. It works! :) I'm just trying to handle the exception for the case when the app cannot find the server (say, the server is down, has a different IP address, or it's not even on the same network). – frankelot May 25 '14 at 19:33

3 Answers3

3

[Not tested, from the top of my head]

Looks like you could intervene here:

Caused by ... yada yada ...
retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)

Retrofit's RestAdapter.Builder lets you customize the way you access your backend with setClient() method, so you can go like this:

RestAdapter restAdapter = new RestAdapter.Builder().setClient(
  new Client.Provider() {
    public Client get() {
      return new UrlConnectionClient() {
          @Override
          public Response execute(Request request) {
            // Test avail. / handle exceptions here,
            // call through to the real implementation
            return super.execute();
          }
      };
    }
  });

For actual host availability logic you can try this recipe

Community
  • 1
  • 1
Ivan Bartsov
  • 19,664
  • 7
  • 61
  • 59
  • thanks!, but I'm pretty new at Retrofit, it's too bad there isn't a good tutorial out the yet. (I'm not quite sure what to do where you wrote **Test Avail**) – frankelot May 25 '14 at 21:43
  • Check the link at the bottom - it shows how to test connectivity, if there's no connection to the server you know it will fail so don't call the super implementation. But if you found another solution that works - great ) – Ivan Bartsov May 25 '14 at 21:48
0

Here's what I ended up doing: I don't know if it's a good solutions but it works. (I'll just have to remember to catchthis exception everytime I use the interface.

        try {
            user = apiInterface.logIn(params[0], params[1]);
            if (user != null) {
                return Constants.LOG_IN_OK;
            } else {
                return Constants.LOG_IN_INVALID_CREDENTIALS;
            }
        } catch (RetrofitError e) {
            return Constants.LOG_IN_SERVER_NOT_FOUND;
        }
frankelot
  • 13,666
  • 16
  • 54
  • 89
0

If you can you may use async call. Exceptions in Retrofit are better handled if you use async call. Asynchronous execution requires the last parameter of the method be a Callback. (http://square.github.io/retrofit/) So your interface would look like this:

public interface RestAppApiInterface {

@FormUrlEncoded
@POST(Constants.URL_LOGIN)
void logIn(@Field("username") String username, @Field("password") String password, Callback<User>pCallback);

Exceptions are handled in Callbacks failure method. And no need to execute it in seperate thread then.

Use:

apiInterface.logIn("username","p@55w0rd", new Callback<User>() {
                @Override
                public void success(User u, Response response) {
                    //on success
                }

                @Override
                public void failure(RetrofitError error) {
                    //on failure
                }
            });