How to get a value from Kotlin AsyncTask - android

I can't get one solution for this. I have searched many things and I can't get an answer. Please help me. This is my code
class NewTask : AsyncTask<Void, Void, String>() {
public override fun doInBackground(vararg params: Void?): String? {
val arr = ArrayList<String>()
val url = URL("http://boogle.org")
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
//arr.add(responseCode)
inputStream.bufferedReader().use {
it.lines().forEach { line ->
//println(line)
arr.add(line as String)
}
}
}
return arr.get(0)
}
public override fun onPostExecute(result: String?) {
//super.onPostExecute(result)
}
}

You can call the get() method of AsyncTask (or the overloaded get(long, TimeUnit)). This method will block until the AsyncTask has completed its work, at which point it will return you the Result.
It would be wise to be doing other work between the creation/start of your async task and calling the get method, otherwise, you aren't utilizing the async task very efficiently.

I had the same problem, but I settled on coroutines. Here is the code I used:
class CoRoutine{
suspend fun httpGet(url: String = "https://boogle.org): String {
val arr = ArrayList<String>()
withContext(Dispatchers.IO) {
val url = URL(url)
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
//arr.add(responseCode)
inputStream.bufferedReader().use {
it.lines().forEach { line ->
//println(line)
arr.add(line as String)
}
}
}
}
return arr.get(0)
}
}
Answer taken from my other answer.

Related

How to get live data from WorkManager in Android

I am trying to make API call from doWork() method of WorkManager. I receive MutableLiveData with list from response. How to set this complex object as output from WorkManager.
Please find below implementation for the same :
class FetchWorkManager(context: Context, params: WorkerParameters): Worker(context,params) {
var postInfoLiveData: LiveData<List<PostInfo>> = MutableLiveData()
#SuppressLint("RestrictedApi")
override fun doWork(): Result {
fetchInfoFromRepository()
//setting output data
val data = Data.Builder()
.putAll(postInfoLiveData)
//.put("liveData",postInfoLiveData)
.build()
return Result.success(data)
}
fun fetchInfoFromRepository(){
val retrofitRepository = RetrofitRepository()
postInfoLiveData = retrofitRepository.fetchPostInfoList()
}
}
Can anyone help me in resolving this issue.
i am not sure but it should be like this :)
workManager?.getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
?.observe(this, Observer {
if (it?.state == null)
return#Observer
when (it.state) {
State.SUCCEEDED -> {
val successOutputData = it.outputData
}
State.FAILED -> {
val failureOutputData = it.outputData
}
}
})
It is not intended behaviour to return result from Worker with LiveData member. The result from the Worker should be returned as a return value of startWork method. To construct Result object with some data ListenableWorker.Result.success method can be used.
const val WORKER_RESULT_INT = "WORKER_RESULT_INT"
class WorkerWithOutput(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// do some work
return Result.success(Data.Builder().putInt(WORKER_RESULT_INT, 123).build())
}
}
And to get this data from outside one of getWorkInfoXXX methods should be used.
fun getResult(context: Context, owner: LifecycleOwner, id: UUID) {
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(id)
.observe(owner, Observer {
if (it.state == WorkInfo.State.SUCCEEDED) {
val result = it.outputData.getInt(WORKER_RESULT_INT, 0)
// do something with result
}
})
}
Activity or fragment can be passed as LifecycleOwner (depending on your case). WorkRequest.getId is used to get id of the work.
It is worth noting that there is ListenableWorker.setProgressAsync which also can be useful in such circumstances.
I am not sure if this would work since I have not tried it yet. and I know it is a late answer but I would encourage you to try to use CoroutineWorker as below:
class MyWorker(context: Context, params: WorkerParameters):
CoroutineWorker(context, params){
override suspend fun doWork(): Result {
val data = withContext(Dispatchers.IO) {
// you can make network request here (best practice?)
return#withContext fetchInfoFromRepository()
// make sure that fetchInfoFromRepository() returns LiveData<List<PostInfo>>
}
/* Then return it as result with a KEY (DATA_KEY) to use in UI. */
val result = workDataOf(DATA_KEY to data)
return Result.success(result)
}
}
ref: https://developer.android.com/topic/libraries/architecture/workmanager/advanced/coroutineworker

New instances of AsyncTask not executing

This Kotlin routine which I cobbled together from various forum examples works BUT ONLY ON THE FIRST CALL.
class myClass: Activity(), myInterface {
override fun onCreate(...) {
...
}
override fun myCallback(response: String) {
myReturningFunction(response)
}
fun myCallingFunction() {
...
...
val myServer = myObject
myServer.myObjectInit(this, stringData)
//myServer.execute(stringData)
}
}
interface myInterface {
fun myCallback(response: String)
}
object myObject : AsyncTask<String, String, String>() {
var thisInterface: myInterface? = null
fun myObjectInit(thatInterface: myInterface, stringData: String) {
thisInterface = thatInterface
//this.executeOnExecutor(THREAD_POOL_EXECUTOR)
this.execute(stringData)
}
override fun doInBackground(vararg params: String): String? {
var response: String = ""
//return try {
try {
params.first().let {
val url = URL("- web service URL -")
val urlConnect = url.openConnection() as HttpURLConnection
with(urlConnect) {
requestMethod = "POST"
readTimeout = 5000
connectTimeout = 5000
doInput = true
doOutput = true
setRequestProperty("Content-Type", "application/json")
setRequestProperty("Accept", "application/json")
setRequestProperty("Charset", "utf-8")
val jsonByteData = it.toByteArray(Charsets.UTF_8)
outputStream.write(jsonByteData, 0, jsonByteData.size)
outputStream.flush()
outputStream.close()
//inputStream.bufferedReader().readText()
response = inputStream.bufferedReader().readText()
inputStream.close()
disconnect()
}
}
} catch (e: Exception) {
response = ""
}
return response
}
override fun onPostExecute(result: String?) {
when {
result != null -> {
thisInterface?.myCallback(result)
}
else -> {
println("null response")
}
}
}
}
I instantiate a copy of the AsyncTask object and execute it, and when I successfully receive the response via the interface, I instantiate another copy (val myServer = myObject) for a follow-up call, but this time it throws this error:
Cannot execute task: the task is already running.
I've tried many approaches, closing the input stream, disconnecting from the server, cancelling the task, but none of it works.
Is there something obviously wrong with the code that I'm missing?
TIA.
An AsyncTask can only be executed once. If you want to execute it again, you'll need to create a second one.
From the documentation:
The task can be executed only once (an exception will be thrown if a
second execution is attempted.)
What you could do is subclass the AsnycTask and use a new instance each time you want to execute it:
fun startBackgroundTask(){
CustomAsyncTask().execute()
// Or in your case:
CustomAsyncTask().myObjectInit(this, "data")
}
class CustomAsyncTask: AsyncTask<String, String, String>(){
var thisInterface: myInterface? = null
fun myObjectInit(thatInterface: myInterface, stringData: String) {
thisInterface = thatInterface
execute(stringData)
}
override fun doInBackground(vararg params: String?): String {
// Do your work.
return ""
}
}

Returning a value from AsyncTask in Kotlin

I'm making an Android app that follows the MVVM pattern using Room and LiveData, but I need to return the ID of the last insertion in one table because is used to add values in other table where the ID is the foreign key, I know that I can achive this returning it from the DAO:
#Dao
interface TratamientoDao {
#Insert
fun insert(tratamiento : Tratamiento): Long
}
But the problem is that I need to return a value from the AsyncTask that inserts the new entry,I tried the approach explained in this question: How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
But In my case the method processFinish is never called.
This is my code:
interface AsyncValue{
fun processFinish(lastID: Long?) : Long?
}
class TratamientoRepository (application: Application): AsyncValue {
val tratamientoDao : TratamientoDao
init{
val database = MMRDataBase.getInstance(application)
tratamientoDao = database.tratamientoDao()
}
fun insert(tratamiento: Tratamiento) : Long? {
InsertTratamientoAsyncTask(tratamientoDao).execute(tratamiento)
return 45
}
override fun processFinish(lastID: Long?): Long? {
Log.d("ValorIngresado:", lastID.toString())
return lastID
}
private class InsertTratamientoAsyncTask constructor(private val tratamientoDao: TratamientoDao) : AsyncTask<Tratamiento, Void, Long>(){
var completionCode : AsyncValue? = null
override fun doInBackground(vararg params: Tratamiento): Long?{
return tratamientoDao.insert(params[0])
}
override fun onPostExecute(result: Long?) {
completionCode?.processFinish(result)
//Log.i("TAMANO", result.toString())
}
}
}
So, How can I return a value from an AsyncTask in Kotlin?
I spent a full day trying to figure this and, co-routines was my solution !!!
First, create your AsyncTask Object ... Do not forget to use corrects parameter type instead all Any
#SuppressLint("StaticFieldLeak")
class AsyncTaskExample(private var activity: MainActivity?) : AsyncTask<Any, Int, Any?>() {
override fun onPreExecute() {
super.onPreExecute()
// do pre stuff such show progress bar
}
override fun doInBackground(vararg req: Any?): Any? {
// here comes your code that will produce the desired result
return result
}
// it will update your progressbar
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
}
override fun onPostExecute(result: Any?) {
super.onPostExecute(result)
// do what needed on pos execute, like to hide progress bar
return
}
}
and Then, call it ( in this case, from main activity )
var task = AsyncTaskExample(this)
var req = { "some data object or whatever" }
GlobalScope.launch( context = Dispatchers.Main){
task?.execute(req)
}
GlobalScope.launch( context = Dispatchers.Main){
println( "Thats the result produced by doInBackgorund: " + task?.get().toString() )
}
There is probably a way, but I prefer using coroutines.
Like for example:
val tratamiento = GetTratamiento()//or whatever you do to get the object
var result = 0
var scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
result = tratamientoDao.insert(tratamiento)
}
Remember to add the coroutines to the app build.gradle
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.27.0-eap13"
This is the way to return a value from AsyncTask you can convert in to Kotlin and try this
public class MyClass extends Activity {
private class myTask extends AsyncTask<Void, Void, String> {
protected Void doInBackground(String... params) {
//do stuff return results;
}
#Override protected void onPostExecute(String result) {
//do stuff
myMethod(myValue);
}
}
private myValueType myMethod(String myValue) {
//handle value
return myValueType;
}
}
The best way to handle AyncTask in Kotlin is using the Anko library for doing background task. It simplifies a lot the use of it. Just like this:
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
For more information check this article:
https://antonioleiva.com/anko-background-kotlin-android/

How can I return a Boolean from AsyncTask?

I have a function that checks network connection then check server availability. If there's network connection it will next check server availability. Unfortunately, checking server availability is through AsyncTask.
This is how I want to use the AsyncTask:
if(NetworkConnectionInfo(context).execute()) {
return true
} else {
return false
}
this is the NetworkConnectionInfo class
class NetworkConnectionInfo : AsyncTask<String, String, Boolean> {
private var context: Context? = null
constructor(context:Context):super(){
this.context = context
}
override fun onPreExecute() {}
override fun doInBackground(vararg p0: String?): Boolean {
try {
val url = URL("http://www.example.com/")
val urlc = url.openConnection() as HttpURLConnection
urlc.setRequestProperty("User-Agent", "test")
urlc.setRequestProperty("Connection", "close")
urlc.setConnectTimeout(1000) // mTimeout is in seconds
urlc.connect()
return urlc.getResponseCode() === 200
} catch (ex:Exception) {
ex.printStackTrace()
}
return false
}
override fun onProgressUpdate(vararg values: String?) {}
override fun onPostExecute(success: Boolean) {
if(!success) {
Toast.makeText(this.context,"Error connecting server. Please try again later.", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this.context,"Server is available.", Toast.LENGTH_LONG).show()
}
}
}
I want to return success in onPostExecute. I don't know how to approach this.
An AsyncTask does not have a return value, since it is asynchronous.
You have to use the result (success) directly in the callback function onPostExecute. This is how AsyncTask is designed and supposed to be used.
override fun onPostExecute(success: Boolean) {
// call further functions depending on "success"
// Note: can access views since it runs on the UI thread
}

AsyncTask in Android with Kotlin

How to make an API call in Android with Kotlin?
I have heard of Anko . But I want to use methods provided by Kotlin like in Android we have Asynctask for background operations.
AsyncTask is an Android API, not a language feature that is provided by Java nor Kotlin. You can just use them like this if you want:
class someTask() : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String? {
// ...
}
override fun onPreExecute() {
super.onPreExecute()
// ...
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
// ...
}
}
Anko's doAsync is not really 'provided' by Kotlin, since Anko is a library that uses language features from Kotlin to simplify long codes. Check here:
https://github.com/Kotlin/anko/blob/d5a526512b48c5cd2e3b8f6ff14b153c2337aa22/anko/library/static/commons/src/Async.kt
If you use Anko your code will be similar to this:
doAsync {
// ...
}
You can get a similar syntax to Anko's fairly easy. If you just wan't the background task you can do something like
class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
handler()
return null
}
}
And use it like
doAsync {
yourTask()
}.execute()
Here is an example that will also allow you to update any UI or progress displayed to the user.
Async Class
class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
init {
execute()
}
override fun doInBackground(vararg params: Void?): Void? {
handler()
return null
}
}
Simple Usage
doAsync {
// do work here ...
myView.post({
// update UI of myView ...
})
}
AsyncTask was deprecated in API level 30. To implement similar behavior we can use Kotlin concurrency utilities (coroutines).
Create extension function on CoroutineScope:
fun <R> CoroutineScope.executeAsyncTask(
onPreExecute: () -> Unit,
doInBackground: () -> R,
onPostExecute: (R) -> Unit
) = launch {
onPreExecute()
val result = withContext(Dispatchers.IO) { // runs in background thread without blocking the Main Thread
doInBackground()
}
onPostExecute(result)
}
Now it can be used on any CoroutineScope instance, for example, in ViewModel:
class MyViewModel : ViewModel() {
fun someFun() {
viewModelScope.executeAsyncTask(onPreExecute = {
// ...
}, doInBackground = {
// ...
"Result" // send data to "onPostExecute"
}, onPostExecute = {
// ... here "it" is a data returned from "doInBackground"
})
}
}
or in Activity/Fragment:
lifecycleScope.executeAsyncTask(onPreExecute = {
// ...
}, doInBackground = {
// ...
"Result" // send data to "onPostExecute"
}, onPostExecute = {
// ... here "it" is a data returned from "doInBackground"
})
To use viewModelScope or lifecycleScope add next line(s) to dependencies of the app's build.gradle file:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
package com.irontec.kotlintest
import android.os.AsyncTask
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GetWeatherTask(this.text).execute()
}
class GetWeatherTask(textView: TextView) : AsyncTask<Unit, Unit, String>() {
val innerTextView: TextView? = textView
override fun doInBackground(vararg params: Unit?): String? {
val url = URL("https://raw.githubusercontent.com/irontec/android-kotlin-samples/master/common-data/bilbao.json")
val httpClient = url.openConnection() as HttpURLConnection
if (httpClient.responseCode == HttpURLConnection.HTTP_OK) {
try {
val stream = BufferedInputStream(httpClient.inputStream)
val data: String = readStream(inputStream = stream)
return data
} catch (e: Exception) {
e.printStackTrace()
} finally {
httpClient.disconnect()
}
} else {
println("ERROR ${httpClient.responseCode}")
}
return null
}
fun readStream(inputStream: BufferedInputStream): String {
val bufferedReader = BufferedReader(InputStreamReader(inputStream))
val stringBuilder = StringBuilder()
bufferedReader.forEachLine { stringBuilder.append(it) }
return stringBuilder.toString()
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
innerTextView?.text = JSONObject(result).toString()
/**
* ... Work with the weather data
*/
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == R.id.action_settings) {
return true
}
return super.onOptionsItemSelected(item)
}
}
link - Github Irontec
This is how I do in my projects to avoid memory leaks:
I created an abstract base Async Task class for Async loading
import android.os.AsyncTask
abstract class BaseAsyncTask(private val listener: ProgressListener) : AsyncTask<Void, Void, String?>() {
interface ProgressListener {
// callback for start
fun onStarted()
// callback on success
fun onCompleted()
// callback on error
fun onError(errorMessage: String?)
}
override fun onPreExecute() {
listener.onStarted()
}
override fun onPostExecute(errorMessage: String?) {
super.onPostExecute(errorMessage)
if (null != errorMessage) {
listener.onError(errorMessage)
} else {
listener.onCompleted()
}
}
}
USAGE:
Now every time I have to perform some task in background, I create a new LoaderClass and extend it with my BaseAsyncTask class like this:
class LoadMediaTask(listener: ProgressListener) : BaseAsyncTask(listener) {
override fun doInBackground(vararg params: Void?): String? {
return VideoMediaProvider().allVideos
}
}
Now you can use your new AsyncLoader class any where in your app.
Below is an example to Show/Hide progress bar & handle Error/ Success scenario:
LoadMediaTask(object : BaseAsyncTask.ProgressListener {
override fun onStarted() {
//Show Progrss Bar
loadingBar.visibility = View.VISIBLE
}
override fun onCompleted() {
// hide progress bar
loadingBar.visibility = View.GONE
// update UI on SUCCESS
setUpUI()
}
override fun onError(errorMessage: String?) {
// hide progress bar
loadingBar.visibility = View.GONE
// Update UI on ERROR
Toast.makeText(context, "No Videos Found", Toast.LENGTH_SHORT).show()
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
I always use this form:
open class LoadingProducts : AsyncTask<Void, Void, String>() {
private var name = ""
override fun doInBackground(vararg p0: Void?): String {
for (i in 1..100000000) {
if (i == 100000000) {
name = "Hello World"
}
}
return name
}
}
You invoke it in the following way:
loadingProducts = object : LoadingProducts() {
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
Log.e("Result", result)
}
}
loadingProducts.execute()
I use the open so that I can call the onPostExecute method for the result.
I spent a full day trying to figure how to get back the result produced by an Async Task : co-routines was my solution !!!
First, create your AsyncTask Object ... Do not forget to use corrects parameter type instead all Any
#SuppressLint("StaticFieldLeak")
class AsyncTaskExample(private var activity: MainActivity?) : AsyncTask<Any, Int, Any?>() {
override fun onPreExecute() {
super.onPreExecute()
// do pre stuff such show progress bar
}
override fun doInBackground(vararg req: Any?): Any? {
// here comes your code that will produce the desired result
return result
}
// it will update your progressbar
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
}
override fun onPostExecute(result: Any?) {
super.onPostExecute(result)
// do what needed on pos execute, like to hide progress bar
return
}
}
and Then, call it ( in this case, from main activity )
var task = AsyncTaskExample(this)
var req = { "some data object or whatever" }
GlobalScope.launch( context = Dispatchers.Main){
task?.execute(req)
}
GlobalScope.launch( context = Dispatchers.Main){
println( "Thats the result produced by doInBackgorund: " + task?.get().toString() )
}
if in the case you want to do it without using Anko and the correct way is to use the following way
open class PromotionAsyncTask : AsyncTask<JsonArray, Void, MutableList<String>>() {
private lateinit var out: FileOutputStream
private lateinit var bitmap: Bitmap
private lateinit var directory: File
private var listPromotion: MutableList<String> = mutableListOf()
override fun doInBackground(vararg params: JsonArray?): MutableList<String> {
directory = Environment.getExternalStoragePublicDirectory("Tambo")
if (!directory.exists()) {
directory.mkdirs()
}
for (x in listFilesPromotion(params[0]!!)) {
bitmap = BitmapFactory.decodeStream(URL(x.url).content as InputStream)
out = FileOutputStream(File(directory, "${x.name}"))
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
out.flush()
out.close()
listPromotion.add(File(directory, "${x.name}").toString())
}
return listPromotion
}
private fun listFilesPromotion(jsonArray: JsonArray): MutableList<Promotion> {
var listString = mutableListOf<Promotion>()
for (x in jsonArray) {
listString.add(Promotion(x.asJsonObject.get("photo")
.asString.replace("files/promos/", "")
, "https://tambomas.pe/${x.asJsonObject.get("photo").asString}"))
}
return listString}
}
and the way to execute it is as follows
promotionAsyncTask = object : PromotionAsyncTask() {
override fun onPostExecute(result: MutableList<String>?) {
super.onPostExecute(result)
listFile = result!!
contentLayout.visibility = View.VISIBLE
progressLottie.visibility = View.GONE
}
}
promotionAsyncTask.execute(response!!.body()!!.asJsonObject.get("promos").asJsonArray)
I use LaunchedEffect in a composable
LaunchedEffect ("http_get") {
withContext (Dispatchers.IO) {
http_get() }}
and rememberCoroutineScope in a callback
val scope = rememberCoroutineScope()
Button (
onClick = {
scope.launch {
withContext (Dispatchers.IO) {
http_get() }}})
It seems to work, but I don't know why.
private fun updateUI(account: GoogleSignInAccount?) {
if (account != null) {
try {
AsyncTaskExample().execute()
} catch (e: Exception) {
}
}
}
inner class AsyncTaskExample : AsyncTask<String, String, String>() {
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg p0: String?): String {
var Result: String = "";
try {
googleToken = GoogleAuthUtil.getToken(activity, accountVal, "oauth2:https://www.googleapis.com/auth/userinfo.profile")
signOut()
} catch (e: Exception) {
signOut()
}
signOut()
return Result
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
socialPrsenter.setDataToHitApiGoogleLogin(googleToken ?: "")
}
}

Categories

Resources