如何修复Kotlin项目中Google Play控制台的NetworkOnMainThreadException问题
修复android.os.NetworkOnMainThreadException错误
问题背景
我的Kotlin项目在Google Play控制台的稳定性模块检测到android.os.NetworkOnMainThreadException错误,相关代码及错误信息如下:
关键错误代码片段
private fun getLocation(latitude: Double, longitude: Double){ try { geoCoder = Geocoder(this@SignUpActivity, Locale.ENGLISH) // 此处触发网络操作,主线程执行导致异常 val addresses = geoCoder.getFromLocation(latitude, longitude, 1) val countryName = addresses?.get(0)?.countryName val countryCode = addresses?.get(0)?.countryCode val state = addresses?.get(0)?.adminArea val city = addresses?.get(0)?.locality val locationData = LocationModelClass( userLatitude = latitude, userLongitude = longitude, userCountryName = countryName, userCountryCode = countryCode, userState = state, userCity = city ) locationViewModel.setUserSignUpLocationData(locationModelClass = locationData) } catch (e: IOException) { e.printStackTrace() } }
错误截图

错误原因
Geocoder.getFromLocation()方法内部可能发起网络请求获取地理编码数据,当前代码直接在**主线程(UI线程)**中调用该方法,违反Android主线程规则——主线程禁止执行耗时网络操作,因此抛出NetworkOnMainThreadException。
修复方案
推荐使用Kotlin协程将地理编码操作移至后台线程执行,完成后切换回主线程更新ViewModel数据,以下提供两种实现方式:
方案1:在Activity中直接用协程修改getLocation方法
private fun getLocation(latitude: Double, longitude: Double){ lifecycleScope.launch(Dispatchers.IO) { try { geoCoder = Geocoder(this@SignUpActivity, Locale.ENGLISH) val addresses = geoCoder.getFromLocation(latitude, longitude, 1) val countryName = addresses?.get(0)?.countryName val countryCode = addresses?.get(0)?.countryCode val state = addresses?.get(0)?.adminArea val city = addresses?.get(0)?.locality val locationData = LocationModelClass( userLatitude = latitude, userLongitude = longitude, userCountryName = countryName, userCountryCode = countryCode, userState = state, userCity = city ) // 切换回主线程更新ViewModel withContext(Dispatchers.Main) { locationViewModel.setUserSignUpLocationData(locationModelClass = locationData) } } catch (e: IOException) { e.printStackTrace() // 主线程提示解析失败 withContext(Dispatchers.Main) { Toast.makeText(this@SignUpActivity, "地理编码解析失败", Toast.LENGTH_SHORT).show() } } } }
方案2:将地理编码逻辑移至ViewModel(符合MVVM架构)
- 修改
LocationViewModel,添加协程处理逻辑:
class LocationViewModel : ViewModel() { private val userLocationData: MutableLiveData<LocationModelClass> = MutableLiveData() fun setUserSignUpLocationData(locationModelClass: LocationModelClass) { userLocationData.value = locationModelClass } fun getUserSignUpLocationData(): MutableLiveData<LocationModelClass> { return userLocationData } // 新增:后台处理地理编码 fun fetchGeocodeData(context: Context, latitude: Double, longitude: Double) { viewModelScope.launch(Dispatchers.IO) { try { val geoCoder = Geocoder(context, Locale.ENGLISH) val addresses = geoCoder.getFromLocation(latitude, longitude, 1) val countryName = addresses?.get(0)?.countryName val countryCode = addresses?.get(0)?.countryCode val state = addresses?.get(0)?.adminArea val city = addresses?.get(0)?.locality val locationData = LocationModelClass( userLatitude = latitude, userLongitude = longitude, userCountryName = countryName, userCountryCode = countryCode, userState = state, userCity = city ) // 主线程更新LiveData withContext(Dispatchers.Main) { userLocationData.value = locationData } } catch (e: IOException) { e.printStackTrace() } } } }
- 在Activity的
startLocationUpdate中调用ViewModel方法:
private fun startLocationUpdate() { locationViewModelLatLong.getLocationData().observe(this, Observer { val la = it.latitude val lo = it.longitude Log.d("SignUpActivity", "getLocationData() it.userLatitude$la") Log.d("SignUpActivity", "getLocationData() it.userLatitude$lo") // 调用ViewModel方法处理地理编码 locationViewModel.fetchGeocodeData(this@SignUpActivity, la, lo) }) }
额外注意事项
- 确保项目已添加协程依赖,在Module级别的
build.gradle中添加:
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" }
Geocoder在部分设备上可能解析失败,建议完善错误处理逻辑,避免影响后续流程。
内容的提问来源于stack exchange,提问作者chanthini begam
相关产品推荐
相关产品推荐

