Android 应用无法连接到本地主机服务

Android app unable to connect to localhost service

我是 android 应用程序开发的新手。我已经创建了一个简单的 hello world android 应用程序,我正在尝试在我的本地计算机上调用 HelloWorldService 运行 的 REST api 端点。该服务是 运行 使用 Spring 在 IntelliJ 中启动。服务代码如下:

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

要从移动应用程序调用此 api,我使用的是 Retrofit 库。代码如下:

依赖关系

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

我试过什么?

  1. 谷歌搜索后,我明白我不能使用localhost作为访问网络服务的IP地址,我必须使用实际的网络IP。此外,笔记本电脑和 phone 需要在同一网络上。因此,我更新了我的基础 url 以使用 inet 地址 x.x.x.x:port_number.
  2. 我已确认两个设备都连接到同一个 Wi-Fi。
  3. 我已经为我的 android 应用程序授予互联网权限。 <uses-permission android:name="android.permission.INTERNET" />
  4. 在笔记本电脑上尝试过 postman 和 web 浏览器。我得到了正确的回应。
  5. 尝试使用移动浏览器调用服务。我无法访问它。
  6. 使用了特殊地址的模拟器10.0.2.2,我可以访问它。

但是,我无法连接到端点并出现以下错误:

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)

感谢任何指点。

如果你是 运行 模拟器上的 android 应用程序,你可以只使用 10.0.2.2 的 ip 默认情况下,这是指模拟器的主机。

如果您使用的是真实设备,请使用真实 IP。这意味着服务器要么需要在 saw WIFI 网络上并且必须设置网络防火墙以允许流量进入,要么您需要一个可公开寻址的 IP 地址。大多数路由器默认设置 NAT,因此您必须在其中挖一个洞。

此外,请确保服务器使用正确的 IP 地址,而不只是绑定到本地主机。