Kotlin使用OkHttp发起网络请求报NetworkOnMainThreadException如何解决
报错原因
Android系统强制禁止在主线程(UI线程)执行耗时的网络请求操作,你代码中调用的client.newCall(request).execute()是同步阻塞方法,直接在主线程运行会触发android.os.NetworkOnMainThreadException异常。
该限制的设计初衷是避免网络请求阻塞主线程导致页面卡顿、无响应(ANR),保障用户交互体验。
解决方案
方案1:使用OkHttp自带异步请求(无额外依赖)
将同步execute()替换为异步enqueue()方法,请求自动在OkHttp管理的子线程执行,结果回调后切回主线程更新UI即可,修改后代码如下:
@SuppressLint("SetTextI18n") fun wordCall() { val textView: TextView = findViewById(R.id.blue) val client = OkHttpClient() // 直接传入字符串url即可,无需额外转为URL类 val url = "https://reqres.in/api/users?page=2" val request = Request.Builder() .url(url) .get() .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 子线程回调,更新UI需切主线程 runOnUiThread { textView.text = "请求失败:${e.message}" } } override fun onResponse(call: Call, response: Response) { val responseBody = response.body?.string() ?: "响应内容为空" // 子线程回调,更新UI需切主线程 runOnUiThread { textView.text = "Response Body: $responseBody" } } }) }
注意事项:
- 回调默认运行在子线程,直接操作UI会报错,必须通过
runOnUiThread、Handler等方式切换到主线程再更新TextView。 - 避免用
!!处理响应体,用空安全调用+默认值可规避空指针崩溃。
方案2:使用Kotlin协程(代码更简洁,推荐)
如果项目已引入Kotlin协程,用协程处理异步逻辑可读性更高,且可以和页面生命周期绑定避免内存泄漏。
首先确认模块级build.gradle已添加协程依赖(已引入可跳过):
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" }
修改后的请求代码:
@SuppressLint("SetTextI18n") fun wordCall() { val textView: TextView = findViewById(R.id.blue) val client = OkHttpClient() val url = "https://reqres.in/api/users?page=2" val request = Request.Builder() .url(url) .get() .build() // 绑定Activity生命周期的协程作用域,页面销毁自动取消请求 lifecycleScope.launch { val result = withContext(Dispatchers.IO) { // 网络请求放在IO线程执行,不阻塞主线程 try { val response = client.newCall(request).execute() response.body?.string() ?: "响应内容为空" } catch (e: Exception) { "请求失败:${e.message}" } } // 自动回到主线程,可直接更新UI textView.text = "Response Body: $result" } }
注意事项:
lifecycleScope需要引入AndroidX Lifecycle KTX依赖,常规AndroidX项目默认已包含。- 耗时的网络请求必须放在
Dispatchers.IO调度的线程执行。
内容的提问来源于stack exchange,提问作者Tyler Thomas
相关产品推荐
相关产品推荐

