Android 我的函数是异步的,为什么会出现NetworkOnMainThread异常?
这是进行所有API调用的对象。有时,当我调用此函数时,会收到NetworkOnMainThread异常。并不是每次都这样。我很困惑,因为我已经使这个函数异步。。。为什么我仍然得到这个例外Android 我的函数是异步的,为什么会出现NetworkOnMainThread异常?,android,kotlin,asynchronous,exception,networking,Android,Kotlin,Asynchronous,Exception,Networking,这是进行所有API调用的对象。有时,当我调用此函数时,会收到NetworkOnMainThread异常。并不是每次都这样。我很困惑,因为我已经使这个函数异步。。。为什么我仍然得到这个例外 object APICaller{ private const val apiKey = "API_KEY_HERE" //Live Data Objects var errorCode = MutableLiveData<Int>() var
object APICaller{
private const val apiKey = "API_KEY_HERE"
//Live Data Objects
var errorCode = MutableLiveData<Int>()
var fetchedResponse = MutableLiveData<Response>()
//Asynchronous network call
suspend fun networkCall(query: String) = withContext(Dispatchers.Default){
val apiURL = "API_URL_HERE"
try{
//Get response
val response = OkHttpClient().newCall(Request.Builder().url(apiURL).build()).execute()
if(response.isSuccessful){
//UI changes (including changes to LiveData values) must be performed on main thread.
Handler(Looper.getMainLooper()).post{
fetchedResponse.value = response
}.also{
Log.i("Response Succ", response.toString())
}
} else {
Handler(Looper.getMainLooper()).post{
errorCode.value =
ToastGenerator.REQUEST_ERROR
}.also{
Log.i("Response Fail", response.toString())
}
}
//Catch any thrown network exceptions whilst attempting to contact API
} catch(e: Exception){
Handler(Looper.getMainLooper()).post{
errorCode.value =
ToastGenerator.NETWORK_ERROR
}.also{
Log.i("Network Fail", e.message.toString())
}
}
}
}
第三个是调用片段函数的活动
fun request(query: String){
browseViewModel.request(query)
progressSpinner?.visibility = View.VISIBLE
}
private fun makeRequest(query: String){
browseFragment.let{
supportFragmentManager.beginTransaction().replace(R.id.fragmentContainer, it).commit()
it.request(query)
}
}
它可能与这些其他功能有关吗
非常感谢您的反馈。提前感谢:)我看不出您的代码中可能出现什么错误,但它肯定会更干净,这可能会解决您的问题,或者至少会更容易找到错误 使用
suspend
函数的一个好策略是将它们设计为总是从主调度器调用。然后,您可以在它们中自由地进行UI调用,并仅使用withContext
包装背景部分。因此,上面的函数可以向下移动withContext()
以仅包装execute()
调用,并且可以删除所有处理程序用法。(顺便说一句,使用withContext(Dispatchers.Main)
会更干净)
然而,改型已经提供了一个挂起函数版本的调用,所以您甚至不需要withContext
包装器。只需使用await()
而不是execute()
,您的挂起函数就会崩溃为:
suspend fun networkCall(query: String) {
val apiURL = "API_URL_HERE"
try{
val response = OkHttpClient().newCall(Request.Builder().url(apiURL).build()).await()
if (response.isSuccessful){
Log.i("Response Succ", response.toString())
fetchedResponse.value = response
} else {
Log.i("Response Fail", response.toString())
errorCode.value = ToastGenerator.REQUEST_ERROR
}
} catch(e: Exception){
Log.i("Network Fail", e.message.toString())
errorCode.value = ToastGenerator.NETWORK_ERROR
}
}
然后,您应该使用viewModelScope
或lifecycleScope
而不是使用GlobalScope,这样您的协同程序就不会泄漏UI组件。上面的wait()
挂起函数支持取消,因此,例如,如果您的ViewModel由于相关片段或活动超出范围而被破坏,您的网络呼叫将自动取消
fun request(query: String) {
viewModelScope.launch {
APICaller.networkCall(query)
}
}
如果问题仍然存在,请仔细研究堆栈跟踪以查看可能出现错误的位置。为什么要将
withContext()
包括在内。您可以使用suspend函数更改它,而不使用和context
,并在调用函数时启动新的协同程序。这可能是一个更好的解决方案
fun request(query: String) {
viewModelScope.launch {
APICaller.networkCall(query)
}
}