Android 我的函数是异步的,为什么会出现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

这是进行所有API调用的对象。有时,当我调用此函数时,会收到NetworkOnMainThread异常。并不是每次都这样。我很困惑,因为我已经使这个函数异步。。。为什么我仍然得到这个例外

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