You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio开发App获取本地服务器数据时偶发崩溃求助

问题:Android App访问本地服务器崩溃,偶尔正常的原因及解决办法

我正在使用Android Studio开发我的第一款App,尝试从本地Web服务器获取数据。代码如下:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        val textView = findViewById<TextView>(R.id.xml_text_view)
        val client = OkHttpClient()
        val url = "http://192.168.43.41"
        val request = Request.Builder().url(url).build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                textView.text = e.toString()
                e.printStackTrace()
            }

            override fun onResponse(call: Call, response: Response) {
                if (response.isSuccessful) {
                    runOnUiThread {
                        textView.text = response.body!!.string()
                    }
                }
            }

        })
    }
}

现象:

  • 打开App时会崩溃,尝试3-4次后又能正常显示数据;
  • 将URL更换为公网地址(如https://reqres.in/api/users?page=2)时可正常运行;
  • 当ESP8266服务器离线时,App也能正常打开。

崩溃日志:

AndroidRuntime          com.example.test1

E  FATAL EXCEPTION: main
Process: com.example.test1, PID: 8062
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1565)
at java.net.SocketInputStream.read(SocketInputStream.java:175)
at java.net.SocketInputStream.read(SocketInputStream.java:144)
at okio.InputStreamSource.read(JvmOkio.kt:93)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:128)
at okio.RealBufferedSource.read(RealBufferedSource.kt:192)
at okhttp3.internal.http1.Http1ExchangeCodec$AbstractSource.read(Http1ExchangeCodec.kt:339)
at okhttp3.internal.http1.Http1ExchangeCodec$UnknownLengthSource.read(Http1ExchangeCodec.kt:475)
at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:281)
at okio.Buffer.writeAll(Buffer.kt:1303)
at okio.RealBufferedSource.readString(RealBufferedSource.kt:96)
at okhttp3.ResponseBody.string(ResponseBody.kt:187)
at com.example.test1.MainActivity$onCreate$2.onResponse$lambda$0(MainActivity.kt:42)
at com.example.test1.MainActivity$onCreate$2.$r8$lambda$wwUQooT56XlASn_Q0N96Hr51lHo(Unknown Source:0)
at com.example.test1.MainActivity$onCreate$2$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
at android.os.Handler.handleCallback(Handler.java:900)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)

原因分析

崩溃根本原因

日志中的NetworkOnMainThreadException明确指出问题:你在主线程中执行了网络IO操作。具体是在runOnUiThread代码块里调用了response.body!!.string()——这个方法并非单纯读取内存数据,当响应流未完全加载到本地时,它会触发实际的网络读取操作,而Android的StrictMode机制会禁止主线程做这类耗时网络操作,直接抛出异常。

特殊现象解释

  1. 偶尔正常:当本地服务器响应速度极快,响应流已经完全加载到OkHttp的缓存内存中,此时string()只是读取内存数据,不会触发网络IO,因此不会触发异常。
  2. 公网地址正常:公网服务器的响应通常经过CDN优化或OkHttp内部缓存处理,响应流提前完成加载,主线程调用string()时无实际网络操作。
  3. 服务器离线正常:此时请求直接走到onFailure回调,仅在主线程更新文本,未涉及ResponseBody的读取操作,因此无异常。

解决方案

核心修复:子线程读取响应体,主线程更新UI

将response.body?.string()的调用移到Callback的子线程中完成,再把读取到的结果传到主线程更新UI,避免主线程处理网络IO:

client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // 更新UI必须在主线程
        runOnUiThread {
            textView.text = e.toString()
        }
        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        if (response.isSuccessful) {
            // 在子线程读取响应内容,避免主线程网络操作
            val responseContent = response.body?.string() ?: "Empty response"
            // 切换到主线程更新UI
            runOnUiThread {
                textView.text = responseContent
            }
        } else {
            runOnUiThread {
                textView.text = "Request failed: ${response.code}"
            }
        }
    }
})

额外注意事项

  • 永远不要在主线程执行任何网络相关的IO操作(包括读取响应体、发起同步请求等)。
  • 避免使用!!强制非空,改用?:处理空值,防止空指针异常。
  • 确认AndroidManifest.xml中已添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />

内容的提问来源于stack exchange,提问作者user28783786

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:23:13