Search code examples
androidspring-bootrestretrofit2

Android app unable to connect to localhost service


I am new to android app development. I have created a simple hello world android app and I am trying to invoke a REST api endpoint of a HelloWorldService running on my local machine. The service is running using Spring Boot in IntelliJ. Service code is as below:

@RestController
public class SimpleController {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String sayHello() {
        return "Hello world";
    }
}

To invoke this api from mobile app, I am using Retrofit library. The code is as below:

Dependencies

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

    compileOnly 'org.projectlombok:lombok:1.18.20'
    annotationProcessor 'org.projectlombok:lombok:1.18.20'

SimpleService.java

public interface SimpleService {
    @GET("/hello")
    Call<String> sayHello();
}

MainActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        TextView textView = findViewById(R.id.textView);

        button.setOnClickListener(view -> {
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10, TimeUnit.SECONDS)
                    .build();

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://x.x.x.x:8080")
                    .client(httpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            final SimpleService simpleService = retrofit.create(SimpleService.class);
            final Call<String> stringCall = simpleService.sayHello();

            stringCall.enqueue(new Callback<String>() {
                @Override
                public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
                    textView.setText(response.body());
                }

                @Override
                public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
                    t.printStackTrace();
                }
            });
        });
    }

What I have tried?

  1. After googling around, I understood that I cannot use localhost as the IP address to access the web-service and I have to use actual network IP. Also, laptop and phone needs to be on the same network. Hence, I updated my base url to use inet address x.x.x.x:port_number.
  2. I have verified that both the devices are connected to same wi-fi.
  3. I have granted internet permissions for my android app. <uses-permission android:name="android.permission.INTERNET" />
  4. Tried postman and web-browser in laptop. I am getting correct response.
  5. Tried to invoke service using mobile browser. I am unable to access it.
  6. Used emulator with special address 10.0.2.2, I am able to access it.

However, I am unable to connect to the endpoint and getting following error:

W/System.err: java.net.SocketTimeoutException: failed to connect to /192.168.0.11 (port 8080) from /192.168.0.26 (port 56860) after 120000ms
W/System.err:     at libcore.io.IoBridge.connectErrno(IoBridge.java:191)
        at libcore.io.IoBridge.connect(IoBridge.java:135)
W/System.err:     at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
W/System.err:     at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
        at java.net.Socket.connect(Socket.java:621)
        at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:71)
        at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:263)
W/System.err:     at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:183)
        at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
        at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108)
        at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:88)
        at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
W/System.err:     at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
W/System.err:     at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
W/System.err:     at okhttp3.RealCall$AsyncCall.execute(RealCall.java:172)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

Any pointers are appreciated.


Solution

  • If you're running the android app on an emulator, you can just use the ip of 10.0.2.2 By default, this refers to the host of the emulator.

    If you're on a real device, use the real IP. This means the server either needs to be on the saw WIFI network AND the network firewall has to be set up to allow traffic to it, or you need a publicly addressable IP address. Most router setups NAT by default, so you'll have to poke a hole in it.

    Also, make sure the server is using the right IP address and isn't just bound to localhost.