Issue
I have to call server through the API call. My API call is AsyncTask with doInBackground()
containing server GET. onPostExecute()
will get result from server and it will return JSON back to my activity.
Problem is, that I'm using this api call inside the method with return parameter.
Some pseudocode here:
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
}
Adding my API Call from API class (custom class):
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)
}
As you can see, I have to extract objects from JSONObject
and convert it to ArrayList<Objects>
(sometimes do other stuff like filtering). So what will happen in my program. As I call getDataFromServer
it will call AsyncTask to my server and after that it will return empty ArrayList. Until result is available in onSuccess
, method is already over.
So I need to wait for onSuccess
and also I have to wait for getObjectsFromJSON()
to be finished. After that I can return my full list of objects.
Is there any way how to achieve this? I cant do synchronous task, because it will fire NetworkOnMainThreadException. I have to do it in second thread, but also my main thread has to wait for results.
Solution
I have to do it in second thread, but also my main thread has to wait for results.
Clearly, these are two contradictory requirements. The point of the NetworkOnMainThreadException
isn't the "network" part, but the fact that it's a blocking network call. You aren't allowed to perform any kind of blocking calls on the UI thread, be it network or anything else.
What you must do is arrange for your splash screen to be removed within the onPostExecute
call instead of the UI callback method where you show it.
I would highly recommend upgrading your project to Kotlin Coroutines because with them you can avoid all the messy callbacks and write sequential-looking code which nevertheless satisfies the rules of the UI thread. As an outline, this is how your code would look:
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
}
}
Answered By - Marko Topolnik
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.