我必须通过API调用来调用服务器。我的API调用是AsyncTask,其中doInBackground()
包含服务器GET。 onPostExecute()
将从服务器获取结果,并将JSON返回我的活动。
问题是,我正在带有返回参数的方法中使用此api调用。
此处有一些伪代码:
private fun getDataFromServer(apiParam: Int): ArrayList<Objects> {
var extractedObjects: ArrayList<Objects> = ArrayList()
api.getDataByParameter(apiParam, object: APICallback{
override fun onError(errorJSON: JSONObject) {
errorLog: errorJSON
}
override fun onSuccess(resultJSON: JSONObject) {
extractedObjects = getObjectsFromJSON(resultJSON)
}
})
return extractedObjects
}
从API类(自定义类)添加我的API调用:
fun getDataByParameter(apiParam: Int, callback: APICallback){
class GetDataAsync(private val dataCallback: APICallback): AsyncTask<Void, Void, JSONObject>() {
override fun doInBackground(vararg p0: Void?): JSONObject {
val server = Server.getInstance(context!!)
return server.doGet(apiURL + apiParam.toString() + "/")
}
override fun onPostExecute(result: JSONObject) {
super.onPostExecute(result)
if (result!!.has("data_objects")){
dataCallback.onSuccess(result)
} else {
dataCallback.onError(result)
}
}
}
GetDataAsync(callback).execute()
}
interface APICallback{
fun onError(errorJSON:JSONObject)
fun onSuccess(resultJSON:JSONObject)
}
如您所见,我必须从JSONObject
中提取对象并将其转换为ArrayList<Objects>
(有时还要进行过滤之类的其他工作)。所以我的程序会发生什么。当我调用getDataFromServer
时,它将调用AsyncTask到我的服务器,此后它将返回空的ArrayList。在onSuccess
中可用结果之前,方法已经结束。
因此,我需要等待onSuccess
,也必须等待getObjectsFromJSON()
完成。之后,我可以返回我的完整对象列表。
有什么办法可以做到这一点?我无法执行同步任务,因为它将触发 NetworkOnMainThreadException 。我必须在第二个线程中执行此操作,但是我的主线程也必须等待结果。
答案 0 :(得分:1)
在doInBackground()中进行网络调用。 从异步任务中的onPostExecute()获取结果。
答案 1 :(得分:1)
我必须在第二个线程中执行此操作,但我的主线程也必须等待结果。
显然,这是两个矛盾的要求。 NetworkOnMainThreadException
的重点不是“网络”部分,而是事实,它是一个阻塞网络调用。不允许在UI线程上执行任何类型的阻止调用,无论是网络调用还是其他操作。
您必须做的是安排在onPostExecute
调用中删除闪屏,而不是在显示它的UI回调方法中移出。
我强烈建议您将项目升级到Kotlin Coroutines,因为有了它们,您可以避免所有混乱的回调,并编写顺序可见的代码,这些代码仍然可以满足UI线程的规则。概述,这就是您的代码的外观:
class StartActivity : AppCompatActivity, CoroutineContext {
lateinit var masterJob: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + masterJob
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
masterJob = Job()
this.launch {
showSplash()
val result = makeNetworkCall()
updateState(result)
hideSplash()
}
}
suspend fun StartActivity.makeNetworkCall() = withContext(Dispatchers.IO) {
Server.getInstance(this).doGet(apiURL + apiParam.toString() + "/")
}
override fun onDestroy() {
super.onDestroy()
job.cancel() // Automatically cancels all child jobs
}
}