I have an activity that calls a class and runs a function in that class like so:
for (i in 0 until questions) {
droll.droolCalls(sign)
}
This can sometimes run forever as it has to generate a bunch of random numbers, so I want it to be able to run in the background. I wrote an AsyncTask that looks like this:
class MyAsync(
private val droll: CreateLayout,
private val questions: Int, private val sign:Int,
var progressBar: ProgressBar,
var layoutProgress: LinearLayout,
var layoutMain: LinearLayout
) :
AsyncTask<String, Int, CreateLayout>() {
var progress = 0
override fun doInBackground(vararg params: String?): CreateLayout {
for (i in 0 until questions) {
droll.droolCalls(sign)
}
return droll
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progressBar.incrementProgressBy(progress * 10)
}
override fun onPostExecute(result: CreateLayout?) {
super.onPostExecute(result)
layoutProgress.visibility = View.GONE
layoutMain.visibility = View.VISIBLE
}
}
However, when i call the drollclass
MyAsync(droll, totalQuestions,sign, progressBar, Loading, mainLayout).execute("hello")
i get an error from other functions that require drool to run.
this one for example insert_point.addView(droll.makeTextView(numberOfQuestions - 1)) gives me a java.lang.IndexOutOfBoundsException: Index: 2, Size: 0 error because insert_point not getting the data from droll because the Async didn't run? however if i take it out of the Async the for loop our of the Async it works fine.
the whole structure looks something like this
class mainclass{
MyAsync(droll, totalQuestions,sign, progressBar, Loading, mainLayout).execute("hello")
insert_point.addView(droll.makeTextView(numberOfQuestions - 1))
class MyAsync(
private val droll: CreateLayout,
private val questions: Int, private val sign:Int,
var progressBar: ProgressBar,
var layoutProgress: LinearLayout,
var layoutMain: LinearLayout
) :
AsyncTask<String, Int, CreateLayout>() {
var progress = 0
override fun doInBackground(vararg params: String?): CreateLayout {
for (i in 0 until questions) {
droll.droolCalls(sign)
}
return droll
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progressBar.incrementProgressBy(progress * 10)
}
override fun onPostExecute(result: CreateLayout?) {
super.onPostExecute(result)
layoutProgress.visibility = View.GONE
layoutMain.visibility = View.VISIBLE
}
}
}
package www.binexmining.co.`in`.binexmining.binexmining.uam.view.activity
import android.app.Activity
import android.os.AsyncTask
import android.os.Bundle
import android.support.annotation.MainThread
import kotlinx.android.synthetic.main.row_referraluser.view.*
import www.binexmining.co.`in`.binexmining.R
class AsynTaskKotlinMain : Activity()
{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_landing)
}
override fun onResume() {
super.onResume()
MyAssyn().execute("Pre-Executing Param Put here...","Param For Doinbackgroun")
}
}
class MyAssyn : AsyncTask<Any, Any, Any>()
{
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg params: Any?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onPostExecute(result: Any?) {
super.onPostExecute(result)
}
}
Related
I want to parse JSON from this URL https://swapi.dev/api/films/
Here my activity_mail.xml
<ListView
android:id="#+id/mylist"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And here my MainActivity
class MainActivity : AppCompatActivity() {
lateinit var pDialog: ProgressDialog
lateinit var listView: ListView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val url="https://swapi.dev/api/films/"
}
inner class AsyncTaskHandler:AsyncTask<String, String, String>() {
override fun onPreExecute() {
super.onPreExecute()
pDialog= ProgressDialog(this#MainActivity)
pDialog.setMessage("Please Wait")
pDialog.setCancelable(false)
pDialog.show()
}
override fun doInBackground(vararg url: String?): String {
//TODO("Not yet implemented")
val res:String
val connection=URL(url[0]).openConnection()as HttpURLConnection
try {
connection.connect()
res=connection.inputStream.use { it.reader().use { reader->reader.readText()} }
}
finally {
connection.disconnect()
}
return res
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
jsonResult(result)
if(pDialog.isShowing())
pDialog.dismiss()
}
private fun jsonResult(jsonString: String?) {
val jsonArray=JSONArray(jsonString)
val list=ArrayList<MyData>()
var i = 0
while (i<jsonArray.length())
{
val jsonObject=jsonArray.getJSONObject(i)
list.add(
MyData(
jsonObject.getString("title"),
jsonObject.getInt("episode_id"),
jsonObject.getString("opening_crawl"),
jsonObject.getString("director"),
jsonObject.getString("producer"),
jsonObject.getString("release_date")
)
)
i++
}
val adapter=ListAdapter(this#MainActivity,list)
mylist.adapter=adapter
}
}
}
The problem is that he dont find my listview "mylist". I put a id but at the end of the file i have this error : "Unresolved reference: mylist"
Maybe something like that ? (ListView)(R.id.mylist).adapter=adapter
R.layout.activity_main change to R.layout.activity_mail
before get to my issue let me tell you how my app works.
I have A little Grocery App and fetch data from an api with retrofit and after that save it to Roomdatabase.
For better Ui experiment I need to implement searchview with an edittext on my main screen .
So , I decide to code a query in my dao and get all data by title filter .
But the problem is that , when I fill the edittext and click on button to get the product that I filter it nothing happened and doesn't any search .
Well , I guess maybe my problem would be with my code that I implement in repository and viewmodel to insert data to roomdatabase . if not , what's wrong with my code ?
I will be appreciated if you look at my code .
and here is my code :
This is room table :
#Entity(tableName = "newTable")
data class RoomEntity(
#PrimaryKey
(autoGenerate = true)
val id : Int? ,
#ColumnInfo val title: String,
#ColumnInfo val image: String
)
Dao :
#Dao
interface RoomDaoQuery {
#Query("SELECT * FROM newTable")
fun getAllProduct () : LiveData<List<RoomEntity>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertDataToDatabase(model : List<RoomEntity>)
#Query("SELECT * FROM newTable WHERE title LIKE '%' || :search || '%'")
fun searchByName(search: String): List<RoomEntity>
}
Repository :
class Repository(private val database: DatabaseRoom) {
fun getAllProduct() = database.GetDao.getAllProduct()
private fun retrofit(): ApiRetrofit {
return Retrofit.Builder()
.baseUrl("http://192.168.43.106/")
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(ApiRetrofit::class.java)
}
suspend fun fettchAllDat(): List<RoomEntity> {
return retrofit().getProduct()
}
suspend fun insertToDatabase(model : List<RoomEntity>) {
database.GetDao.insertDataToDatabase(fettchAllDat())
}
// this is for local search
fun searchWithName (title : String) : List<RoomEntity> {
return database.GetDao.searchByName(title)
}
}
Viewmodel:
class ViewmodelRoom(application: Application) : AndroidViewModel(application) {
val product = MutableLiveData<List<RoomEntity>>()
private val repository = Repository(DatabaseRoom.getInstance(application))
private var viewModelJob = SupervisorJob()
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Default)
fun getAllProduct() = repository.getAllProduct()
fun setup() {
viewModelScope.launch{
product.postValue(repository.fettchAllDat())
insertall()
}
}
fun insertall() {
viewModelScope.launch {
repository.insertToDatabase(repository.fettchAllDat())
}
}
fun searchByTitle(title : String) = CoroutineScope(Dispatchers.Default).launch{
repository.searchWithName(title)
}
}
and MainActivity :
class MainActivity : AppCompatActivity() {
val viewModel: ViewmodelRoom by lazy {
ViewModelProvider(this).get(ViewmodelRoom::class.java)
}
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText: EditText = findViewById(R.id.edittext)
val search: ImageView = findViewById(R.id.searchview)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
search.setOnClickListener {
viewModel.searchByTitle(editText.text.toString())
editText.text.clear()
}
editText.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
viewModel.searchByTitle(editText.text.toString())
}
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
if (isNetworkAvaliable(applicationContext)) {
viewModel.setup()
viewModel.product.observe(this, Observer {
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(it, this#MainActivity)
}
})
} else {
viewModel.getAllProduct().observe(this, Observer { list ->
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(list, this#MainActivity)
}
})
}
}
finally I get to a proper result .
I put my code here , I hope maybe useful for someone .
the Dao :
#Query("SELECT * FROM newTable WHERE title LIKE :name")
fun search (name : String) :LiveData<List<RoomEntity>>
Repository :
fun search(name : String): LiveData<List<RoomEntity>>{
return database.GetDao.search(name)
}
fun search(name : String) : LiveData<List<RoomEntity>> {
return repository.search(name)
}
MainActivity :
val editText: EditText = findViewById(R.id.edittext)
val search: ImageView = findViewById(R.id.searchview)
recyclerView = findViewById(R.id.recyclerview)
search.setOnClickListener {
// this is an extention function that observe data
searchProduct(editText.text.toString())
}
editText.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
searchProduct(editText.text.toString())
}
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
private fun searchProduct(title : String) {
var searchText = title
searchText = "%$title%"
viewModel.search(searchText).observe(this#MainActivity , Observer {
d("main" , "$it")
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(it, this#MainActivity)
}
})
}
I am trying to do an app where I want to update a ProgressBar in a for loop using AsyncTask. The best is to show you a pseudo code of what I am trying to do
button.setOnClickListener{
for(int i=0; i<5000; i++)
{
doSomeHeavyStuff();
UpdateProgressBarAsyncTask(i).execute()
}
}
This is what I have so far
MainActivity:
class MainActivity : AppCompatActivity() {
var progress:ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
progress = findViewById(R.id.progress)
val buttonStart = findViewById<TextView>(R.id.button)
var maxNumber = 5000
buttonStart.setOnClickListener{
for(i in 0 until maxNumber)
{
HeavyStuff()
ProgressTask(i,progress!!,maxNumber,this).execute()
}
}
}
internal class ProgressTask (var actual:Int, var progress: ProgressBar, var max: Int, var context: Activity): AsyncTask <Void, Int, Int>()
{
override fun onPreExecute() {
super.onPreExecute()
progress.visibility = View.VISIBLE
progress.max = max
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progress.setProgress(values[0]!!)
}
override fun doInBackground(vararg params: Void?): Int? {
publishProgress(actual)
return null
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
progress.visibility = View.INVISIBLE
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show()
}
}
XML:
<ProgressBar
android:id="#+id/progress"
style="#android:style/Widget.ProgressBar.Horizontal"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="24dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Do I miss something here? Right now it shows ProgressBar after the HeavyStuff() is finished and thus it is not shown during the for loop. What am I missing here?
Can you guys please help me with this?
Thanks
Actually, I think that both the heavy stuff and the For Loop need to be present inside of the doBackground function (The call of heavy stuff in the main thread will freeze the UI and cause an ANR), see the code below :
const val max = 50000
class MainActivity : AppCompatActivity() {
var progress: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
progress = findViewById(R.id.progress)
buttonStart.setOnClickListener {
ProgressTask(progress!!, max, this).execute()
}
}
internal class ProgressTask(
var progress: ProgressBar,
var max: Int,
var context: Activity
) : AsyncTask<Void, Int, Int>() {
override fun onPreExecute() {
super.onPreExecute()
progress.visibility = View.VISIBLE
progress.max = max
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progress.setProgress(values[0]!!)
}
override fun doInBackground(vararg params: Void?): Int? {
for (i in 0 until max) {
doHeavyStuff()
publishProgress(i)
}
return null
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
progress.visibility = View.INVISIBLE
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show()
}
}
}
I would go with this approach to avoid memory leaks:
private lateinit var mTextView: WeakReference<TextView>
private lateinit var mProgressBar: WeakReference<ProgressBar>
private const val MAX = 50000
class ProgressTask(
pb: ProgressBar,
var max: Int
) : AsyncTask<Void, Int, Int>() {
init {
mProgressBar = WeakReference(pb)
}
override fun onPreExecute() {
super.onPreExecute()
mProgressBar.get()?.visibility = View.VISIBLE
mProgressBar.get()?.max = MAX
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
mProgressBar.get()?.progress = values[0]!!
}
override fun doInBackground(vararg p0: Void?): Int {
for (i in 0 until MAX) {
doHeavyStuff()
publishProgress(i)
}
return 1
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
mProgressBar.get()?.visibility = View.GONE
}
}
I'm new here and this is my first post!
I try to learn Android app development and I'm stuck with this problem :
How can I update a textview inside a function? I code a small app which generates all the permutation with repetitions and write it in a textview.
It works, but the textview updates only at the end of all the permutations... Don't understand why...
Sorry if my English is bad, I'm French ;)
I try to use Thread, the app doesn't crash, it seems to work but the app goes directly in the background...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
zoneTexte.text = newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
private fun permutation2(text: String, k: Int) {
permutation1(text, "", text.length - 1, k)
}
}
Functions for permutations work well but the textview update only at the end (with the last permutation "hhhhhhhh") and I would like to update it for each permutation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation2(text: String, k: Int) {
MyCal().execute(text)
}
inner class MyCal : AsyncTask<String ,String, String>(){
override fun onProgressUpdate(vararg values: String?) {
super.onProgressUpdate(*values)
zoneTexte.text = values[0]
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
zoneTexte.text = result
}
override fun doInBackground(vararg p0: String?): String {
return permutation1(p0[0]!!, "", p0[0]!!.length?.minus(1), 8)
}
fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
onProgressUpdate(newprefix)
return newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
}
}
onCreate is executed on the ui-thread, as is the case for permutation1() and permutation2(). The ui won't actually refresh until onCreate completes and ui can then refresh/redraw the screen, so that's why you don't see any incremental text updates until the end.
If you would like to see it update in real time, you may want to look into AsyncTask. In your particular example, you aren't really performing a long running task, so I'm not sure if you'll be able to see the incremental additions to your TextView even if you use AsyncTask.
After the help from Mark and Kishan I find the solution ! Thank you guys !
Here is the code :
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation2(text: String, k: Int) {
MyCal().execute(text)
}
inner class MyCal : AsyncTask<String ,String, String>(){
override fun onProgressUpdate(vararg values: String?) {
super.onProgressUpdate(*values)
runOnUiThread(Runnable { zoneTexte.text = values[0] })
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
runOnUiThread(Runnable { zoneTexte.text = result })
}
override fun doInBackground(vararg p0: String?): String {
return permutation1(p0[0]!!, "", p0[0]!!.length?.minus(1), 8)
}
fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
onProgressUpdate(newprefix)
return newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
}
}
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 ?: "")
}
}