I have an issue with my android app. I'm fairly new with it and have some issues with finding the correct documentation for a asynchronous. I'm using the kohttp library to help me a bit.
The thing is, you can't run this on the main UI thread so I want to make this request Async. I can't find a clear reference in the documentation and I don't really know how to do this in plain Kotlin.
This is what I come up with; in a separate class named LoginCall. I tried other answers, this however didn't result in success. How can I run this on a new thread and still use the response?
class LoginCall {
fun callLoginRequest(a:String, b:String): Any {
val response: Response = httpPost {
host = "XXX"
path = "XXX"
param { }
header { }
body {
form {
"email" to a
"password" to b
}
}
}
return response
}
}
There are many ways to achieve this, if you're using android as the underlying platform, you can use the native component called AsyncTask a good SO post on how to use it.
If you wish to leverage kotlin as a language and the features provided by it, you can try using coroutines ref.
Personally, i would recommend coroutines, it simplifies exception and error handling, also prevents callback hell.
here's a sample of the same code in a coroutine,
// global
private val mainScope = CoroutineScope(Dispatchers.MAIN + SupervisorJob())
// inside a method
mainScope.launch{
withContext(Dispatchers.IO){
// do your async task here, as you can see, you're doing this in an IO thread scope.
}
}
Also, you can create an issue to implement asyncHttpPost.
Since kohttp 0.10.0 you can use async methods for such cases. You can try them.
Code example:
suspend fun callLoginRequest(a:String, b:String): Any {
val response: Differed<Response> = httpPostAsync {
host = "XXX"
path = "XXX"
param { }
header { }
body {
form {
"email" to a
"password" to b
}
}
}
// for further usage in coroutines
return response.await()
}
And call this function from coroutine
Related
I have still a little bit of trouble putting all information together about the thread-safety of using coroutines to launch network requests.
Let's say we have following use-case, there is a list of users we get and for each of those users, I will do some specific check which has to run over a network request to the API, giving me some information back about this user.
The userCheck happens inside a library, which doesn't expose suspend functions but rather still uses a callback.
Inside of this library, I have seen code like this to launch each of the network requests:
internal suspend fun <T> doNetworkRequest(request: suspend () -> Response<T>): NetworkResult<T> {
return withContext(Dispatchers.IO) {
try {
val response = request.invoke()
...
According to the documentation, Dispatchers.IO can use multiple threads for the execution of the code, also the request function is simply a function from a Retrofit API.
So what I did is to launch the request for each user, and use a single resultHandler object, which will add the results to a list and check if the length of the result list equals the length of the user list, if so, then all userChecks are done and I know that I can do something with the results, which need to be returned all together.
val userList: List<String>? = getUsers()
val userCheckResultList = mutableListOf<UserCheckResult>()
val handler = object : UserCheckResultHandler {
override fun onResult(
userCheckResult: UserCheckResult?
) {
userCheckResult?.let {
userCheckResultList.add(
it
)
}
if (userCheckResultList.size == userList?.size) {
doSomethingWithResultList()
print("SUCCESS")
}
}
}
userList?.forEach {
checkUser(it, handler)
}
My question is: Is this implementation thread-safe? As far as I know, Kotlin objects should be thread safe, but I have gotten feedback that this is possibly not the best implementation :D
But in theory, even if the requests get launched asynchronous and multiple at the same time, only one at a time can access the lock of the thread the result handler is running on and there will be no race condition or problems with adding items to the list and comparing the sizes.
Am I wrong about this?
Is there any way to handle this scenario in a better way?
If you are executing multiple request in parallel - it's not. List is not thread safe. But it's simple fix for that. Create a Mutex object and then just wrap your operation on list in lock, like that:
val lock = Mutex()
val userList: List<String>? = getUsers()
val userCheckResultList = mutableListOf<UserCheckResult>()
val handler = object : UserCheckResultHandler {
override fun onResult(
userCheckResult: UserCheckResult?
) {
lock.withLock {
userCheckResult?.let {
userCheckResultList.add(
it
)
}
if (userCheckResultList.size == userList?.size) {
doSomethingWithResultList()
print("SUCCESS")
}
}
}
}
userList?.forEach {
checkUser(it, handler)
}
I have to add that this whole solution seems very hacky. I would go completely other route. Run all of your requests wrapping those in async { // network request } which will return Deferred object. Add this object to some list. After that wait for all of those deferred objects using awaitAll(). Like that:
val jobs = mutableListOf<Job>()
userList?.forEach {
// i assume checkUser is suspendable here
jobs += async { checkUser(it, handler) }
}
// wait for all requests
jobs.awaitAll()
// After that you can access all results like this:
val resultOfJob0 = jobs[0].getCompleted()
I am trying to write a simple app in Android Studio using Kotlin. It is a very steep learning curve for me, but I am almost there. My final problem is getting the app to wait for the APIs to complete before moving the next Intent.
I have three calls each uploading data via my API. They are called from a button and only when the three are uploaded, should the button send the user to the next intent/screen.
My API calls are working and I can see the data in the database. However, since enqueue is asynchronous the calls are firing and the code is moving on the start the next intent before the data is present.
The code below is executed 3 times (once for each upload). I realise this is probably not the best way to do it, but I'm trying to get it working before I finesse the code.
I thought that perhaps I could have a variable, UploadedReadCount, that I increment in the onResponse, but this doesn't seem to be working properly.
Could someone offer some advice as to how I should be pausing the code until the APIs complete? For example, is there an enqueue methos that isn't async?
ReadInterface.create().AddRead("new", rFuel, rRegister, rReadDate, rRead)
.enqueue(object : Callback<UploadedRead> {
override fun onFailure(call: Call<UploadedRead>, t: Throwable) {
Log.d("Err: ", t.localizedMessage!!)
t.printStackTrace()
}
override fun onResponse(call: Call<UploadedRead>, response: Response<UploadedRead>) {
Log.d("Response: ", response.body().toString())
val p = response.body()?.APIResult!![0]
msgShow("Gas read " + rRead.toString() + " uploaded")
UploadedReadCount += 1
}
})
while ( UploadedReadCount < 3) {
Log.d("Waiting ", UploadedReadCount.toString() + " reads uploaded...")
}
val intent = Intent(this, Billing::class.java).apply {
putExtra("ReadDate", txtReadDate.text.toString())
}
startActivity(intent)
In most cases you don't want to pause execution while API call returns, Instead you want to follow the reactive model, that is when you call API you specify some callbacks (onResponse, onFailure), and once these callbacks are invoked then you react.
code is moving on the start the next intent before the data is
present.
Move all of your code that depends on data received from API in onResponse or onFailure methods (callbacks), When API is ready with some response one of those callbacks will be invoked and then depending on the data that you receive from API you can continue your work.
is there an enqueue methos that isn't async?
There are options available to call an API in blocking manner but I don't think that is good idea. Instead of doing a blocking API call, you should try to do reactive programming that is as soon as any callback (onResponse, onFailure) is called only then you continue.
There is an alternative to enqueue that is suspending instead of async, so you can call your code sequentially without blocking the main thread in a coroutine. The function is await() and it returns the successful result or throws an HttpException on failure.
But to run three requests in parallel, you need to use the async coroutine builder. This can be done by mapping a list of Calls to async calls that await the individual results, and then using awaitAll() on the list of Deferreds to wait for all three. So, it's more complicated than just running sequential code in a coroutine, but I think this is still easier than trying to run and wait for three parallel calls using callbacks.
I'm not exactly sure what your other two calls are so I'll just make up some and assume this function already has all the data it needs to make the calls. I also don't know how you want to handle failure, so I'm just making it stop early if any of the three calls fail.
lifecycleScope.launch {
val requests: List<Call<UploadedRead>> = listOf(
ReadInterface.create().AddRead("new", rFuel, rRegister, rReadDate, rRead),
ReadInterface.create().AddRead("new2", rFuel, rRegister, rReadDate, rRead),
ReadInterface.create().AddRead("new3", rFuel, rRegister, rReadDate, rRead)
)
val responses: List<UploadedRead> = try {
coroutineScope { // any failure in this block cancels them all
requests.map { async { it.await() } } // run them simultaneously with async
.awaitAll()
}
} catch (e: HttpException) {
Log.d("Err: ", e.localizedMessage.toString())
printStackTrace(e)
return#launch
}
// Do something with the list of three UploadedReads here.
}
I just duplicated the functionality of your code above, but it doesn't look like you're using the response for anything and you have an unused variable p.
Edit: If this is a pattern you use frequently, this helper function might be useful. I didn't check this thoroughly or test it.
/**
* Await the result of all the Calls in parallel. Any exception thrown by any item
* in the list will cancel all unfinished calls and be rethrown.
*/
suspend fun <T: Any> Iterable<Call<T>>.awaitAll(): List<T> =
coroutineScope { map { async { it.await } }.awaitAll() }
//...
lifecycleScope.launch {
val requests: List<Call<UploadedRead>> = listOf(
//...
)
val responses: List<UploadedRead> = try {
requests.awaitAll()
} catch (e: HttpException) {
//...
return#launch
}
//...
}
i'm trying to fix the NetworkOnMainThreadException, here is my class located in a library
class CoinProvider(private val context: Context, isTestnet: Boolean) {
val result = URL("http://mylink.com/file.json").readText()
val result1 = URL("http://mylink.com/file1.json").readText()
private val filename: String = if (!isTestnet) (result) else (result1)
fun defaultCoins(): CoinResponse {
return CoinResponse.parseFile(context, filename)
}
}
when i try to add this to my class
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
i got this " expecting member declaration "
the code above can be added to the MainActivity of the project but it doesn't do anything because i want to fix the NetworkOnMainThreadException on the library not in the project.
how can i fix the issue in this kotlin library?
Oh No. Request network and query database always not calling in Ui thread because it blocks the UI thread (Ui thread always redraws with 16ms time).
Solution thread:
Thread(Runnable {
...// Something Call
}).
This way you need to either implement a callback to handle the outgoing data, or use Handler
Solution RxJava:
Single.fromCallable {
try {
/// Something Doing
} catch (e: IOException) {
false
}
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
With RxJava you can easily work with threads and return results in Mainthread, all you need to do is just Observer the output data.
Solution Kotlin-Coroutines
suspend func getJSON(){
//Something Doing....
}
My understanding will probably help you to solve the problem.
I am building a client application which uses Firebase for two things:
User Authentication
Using a realtime database
I have managed to set up everything correctly on my client and on my backend server (using Firebase's Admin SDK) and am able to correctly authenticate users and allow them to read/write to the database.
I am also using Retrofit2 to send requests from the client to the backend.
As part of allowing users access to the database, it is needed to send the user's token to the backend so the user can be verified.
To do this, I have the following logic:
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
user.getIdToken(false).addOnCompleteListener {
if (it.isSuccessful) {
val token = it.result?.token
//retrofit logic to send request happens from here
}
}
As you can see, getting the Id token of the user is an asynchronous call and in the current code base that I have, I have this code block for each one of my calls to the backend (duplication).
I want to know how I can export this snippet to a function (maybe a suspend method?) so that it can be reused for every call to the backend
I have searched online and have seen many SO questions, but none that fit this scenario.
I have thought about passing in a callback, but I have several methods that communicate to the backend, and each of them will require a different callback method.
The solution I am looking for looks something like this:
fun fetchDataFromDB() {
getIdTokenForUser()
//wait till it finishes and then
//perform request to DB
}
fun updateDataInDB() {
getIdTokenForUser()
//wait till it finishes and then
//perform request to DB
}
//......
I have tried reading about and implementing coroutines, but I lack the knowledge to do so correctly.
EDIT
Thanks to #Doug Stevenson for his answer and direction, I have managed to construct the following:
private suspend fun getUserIdToken(user: FirebaseUser) = coroutineScope {
val job = async {
user.getIdToken(false).result?.token
}
job.await()
}
And I use it in this fashion:
fun updateDB(context: Context) = runBlocking {
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
val token = getUserIdToken(user)
}
}
Is this the correct approach? Since the answers given below present a different implementation.
getIdToken is asynchronous returns a Task object. If you want to use a Task object in a Kotlin coroutine, you can use the library kotlinx-coroutines-play-services to add an extension method await() to the Task that makes it usable in a coroutine. With that, you can write something like this:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.9"
import kotlinx.coroutines.tasks.await
suspend fun getIdTokenForUser(user: FirebaseUser): GetTokenResult {
return try {
user.getIdToken(false).await()
}
catch (e: Exception) {
// handle error
}
}
You might have to update the types here - I didn't try to compile or test this.
See also:
Android kotlin task to be executed using coroutines
Coroutines And Firebase: How to Implement Javascript-like Promise.all()
Using Firebase with Kotlin coroutines
In order to go from a callback based API like the following one:
val myCallback = object : ServiceCallback() {
override fun onResult(theobject: Something) {
// your callback code here
}
override fun onFailure(ex: Throwable) {
// error handling
}
}
theService.enqueue(callback)
You can use suspendCoroutine
What it does is that it suspends execution until the continuation is satified by the callback. So you can write a KTX like the following:
suspend fun Service.getSomething(): Something = suspendCoroutine{ cont ->
val callback = object : ServiceCallback(){
override fun onSuccess(data: Something): Unit = cont.resume(data)
override fun onFailure(ex: Throwable): Unit = cont.resume(ex)
}
this.enqueue(callback)
}
i want to ask a good developers. Maybe anyone can better explaine. Somewhere in web i found that few authors used coroutines instead for example asynctasks . Just trying to improve myself . Here a small part of code which i used . Just want to know - it's good or no. If no - how to make it's better or maybe in final im using this in wrong way.
fun demoCall(callback: OnResponse) {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
Api.getResponse("GET", ApiConstants.test_endpoint)//networkOnMainThread exception if i will not use withContext
}
callback?.onResponse(result))
}
}
This example is work . But im not sure it's good usage.
If back to past ,
getResponse
was in asyncTask. Call was same with annonymus callback.
If to use this way is good , looks like i can use this part without callback ?
Just like this
fun demoCall() {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
Api.getResponse("GET", ApiConstants.test_endpoint)
}
//do anything with result
//populate views , make new response etc..
}
will be very happy if any tell me - is it ok or no :) Regards
I prefer making asynchronous calls be seen like synchronous in caller's view using suspend keyword.
For example,
suspend fun demoCall(): String {
return withContext(Dispatchers.IO) {
Api.getResponse("GET", ApiConstants.test_endpoint) // let's assume it would return string
}
}
and caller can use it
CoroutineScope(Dispatchers.Main).launch {
val result = demoCall() //this is async task actually, but it seems like synchronous call here.
//todo something with result
}