I want to post a runnable to a view object inside a runnable , currently i am stuck here.
var runnable = Runnable {
if(numLinesToDraw >= amplititudes.size){
}
else
{
numLinesToDraw = numLinesToDraw ++
invalidate()
postDelayed({
},2000)
}
}
postDelayed(runnable,2000)
As you can see , there is a postDelayed method inside the runnable. What i want to do is post the same runnable again and so on. What should i add here?
postDelayed({
},2000)
In Kotlin 1.2+, you can define a local lateinit var for the runnable and then ititialize it with a Runnable that uses the variable:
lateinit var runnable: Runnable
runnable = Runnable {
/* ... */
postDelayed(runnable,2000)
}
Do like this,
var myRunnable: Runnable = object : Runnable {
override fun run() {
progressHandler.postDelayed(this, 100) // here is self calling
}
}
Related
I want to call my function indefinitely every 1 second in a specific situation. I'm using AsyncTask to execute my API calls.
I'm using this type of call for some time but this is the first time when it actually blocked my UI Thread and I don't know why.
The handler in the code below is called inside onPostExecute.
protected fun purchaseCheck(transactionId: String){
app.sysLog("Wait for purchase...")
task = asyncTask({
api.checkPaymentStatus(transactionId)
}, taskName = "Purchase Status") { r ->
r.js?.let {
when(r.httpCode){
HTTP_PAYMENT_CHECK_PENDING -> {
App.log("purchaseCheck: response pending purchase - try again")
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
}
else -> {
App.log("purchaseCheck: response purchase success")
onPurchaseSuccessfullyCompleted()
}
}
}?:kotlin.run {
when(r.httpCode){
HTTP_PAYMENT_CARD_EXPIRED -> {
App.log("purchaseCheck: response card expired")
showApiErrorAndRetry(r, App.getString("err_purchase_card_expired"))
}
else -> {
App.log("purchaseCheck: response error (retry)")
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
}
}
}
}
}
Basically
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
is causing my ProgressBar animation to freeze. When I remove that delay it is working as intended.
Here is afterDelay function:
fun afterDelay(delay: Int, body: () -> Unit): Cancellable {
class DelayRun : Runnable, Cancellable {
override fun run() = body()
override fun cancel() {
removePost(this)
}
}
return DelayRun().also {
post(delay, it)
}
}
fun removePost(runnable: Runnable) {
App.handler.removeCallbacks(runnable)
}
fun post(delay: Int, runnable: Runnable){
App.handler.postDelayed(runnable, delay.toLong())
}
Handler in Application class:
class App : Application(), Application.ActivityLifecycleCallbacks{
companion object {
val handler = Handler()
}
...
}
Edit:
After suggestion from post below I implemented Handler like this:
class App : Application(), Application.ActivityLifecycleCallbacks{
companion object {
val handler: Handler by lazy {
HandlerThread("MyHandlerThread").let {
it.start()
Handler(it.looper)
}
}
}
...
}
but it is still freezing my UI Thread. (ProgressBar is lagging)
By default, Handler posts tasks on Main (UI) thread. Therefore any job/task you send to your handler will be executed on UI thread - that is the reason why UI freezes - it waits for job to finish before redrawing.
You want to make your handler using another thread. The most simple way is to create HandlerThread.
val handlerThread = new HandlerThread("MyHandlerThread")
handlerThread.start()
val looper = handlerThread.getLooper()
val handler = new Handler(looper)
After these four lines of code, handler will execute it jobs on another thread. But let's take a problem further - you are using AsyncTask, which is deprecated. You also do not want your delay to be counted by afterDelay function, handler can do it for you.
In your case you can just do something like this:
handler.postDelayed(1000, { ... your job ... }).
Getting it together:
protected fun purchaseCheck(transactionId: String){
app.sysLog("Wait for purchase...")
val runnable = {
val status = api.checkPaymentStatus(transactionId)
status.js?.let { ... }
}
handler.postDelayed(1000, runnable)
}
I also recommend you to declare handler on Activity level rather than Application. Since you usually don't want it to be global.
I am looking a way to launch a thread that would make a GUI element blink forever, in pseudo-code:
while (true) {
GUI element ON
wait for 1s
element OFF
wait for 1s
}
I'm thinking of a recursive chain of handlers, like this:
val handler = Handler()
handler.postDelayed({
Handler().postDelayed({ gui_element_on() }, 1000)
Handler().postDelayed({ gui_element_off() }, 1000)
handler.postDelayed(this,0)
}, 1000)
Only that this won't work. I don't know how to do it properly. And I am not sure if this is the most efficient way.
I've found this recursive solution:
val handler : Handler = Handler()
val run: Runnable = object : Runnable {
override fun run() {
gui_element_on()
handler.postDelayed({gui_element_off()}, 1000)
handler.postDelayed(this, 2000)
}
}
run.run()
It does the trick in this case.
you can use CoroutineScope.launch
Here, I use lifecycleScope extension property from Android Jetpack.
class MyFragment : Fragment() {
override fun onViewCreated(...) {
viewLifecycleOwner.lifecycleScope.launch {
while(isActive) {
GUIElementOn()
delay(1000)
GUIElementOff()
delay(1000)
}
}
}
}
I'm beginner in kotlin. I try to create a task that will repeat every 2 seconds. So I created something like this.
val handler = Handler()
handler.postDelayed(Runnable {
// TODO - Here is my logic
// Repeat again after 2 seconds
handler.postDelayed(this, 2000)
}, 2000)
But in postDelayed(this) it gives error - required Runnable!, found MainActivity. I've tried even this#Runnable but it didn't work.
But when I write the same function like this, it works
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
// TODO - Here is my logic
// Repeat again after 2 seconds
handler.postDelayed(this, 2000)
}
}, 2000)
So why the this keyword doesn't work in first function, but in second function it works good?
You have several options to go about here:
make both the runnable and the handler be in the same scope
//class scope
val handler = Handler()
val runnable = object : Runnable {
override fun run () {
handler.removeCallbacksAndMessages(null)
//make sure you cancel the
previous task in case you scheduled one that has not run yet
//do your thing
handler.postDelayed(runnable,time)
}
}
then in some function
handler.postDelayed(runnable,time)
You can run a timertask, which would be better in this case
val task = TimerTask {
override fun run() {
//do your thing
}
}
val timer = Timer()
timer.scheduleAtFixedRate(task,0L, timeBetweenTasks)
The first one is a function that accepts a lambda and returns a Runnable. In this case this means nothing.
The second one you're defining an anonymous object that implements Runnable. In this case this refers to that object instance.
The below example will work.
val runnable = object : Runnable {
override fun run() {
handler.postDelayed(this,1000)
}
}
In your case , when use this it means "local final class <no name provided> : Runnable" , refer to a header runnable.
runnable=object : Runnable {
override fun run() {
// i is a counter
println("No. "+i++)
// Repeat again after 2 seconds
handler.postDelayed(this, 2000)
}
}
handler.postDelayed(runnable,0)
Where runnable is used inside of a method. As Handler() is deprecated, we must use like this:
var handler: Handler = Handler(Looper.getMainLooper())
var runnable: Runnable = Runnable { }
Moreover, anywhere you can stop this method by:
handler.removeCallbacks(runnable)
I am trying to code in kotlin android to move an image every second but I am not able to make it work. Right now I'm using a Timer to schecule a Timer Task every second but it is not working as expected.
Here is my code
class Actvt_Image<float> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_actvt__image)
val pict_mario = findViewById<ImageView>(R.id.img_Mario)
val bt_down = findViewById<Button>(R.id.bt_down)
val frame = findViewById<LinearLayout>(R.id.frame)
val txt1=findViewById<TextView>(R.id.txt1)
var i =100
val timer = Timer()
val myTask = object : TimerTask() {
override fun run() {
txt1.text = (i+1).toString()
img_Mario.rotation=180f
img_Mario.translationX +=100
img_Mario.translationY +=20
}
}
bt_down.setOnClickListener {
i=0
timer.schedule(myTask, 1000, 1000)
}
}
}
You are trying to update the UI on a background thread which is not possible. UI can only be updated on the UI thread. Also, using a Timer and TimerTask to create and destroy a thread every 1 second is not the right way to use threads because creating a thread is a memory expensive operation.
What you need to do is to use a Handler and tell the UI Thread to run a Runnable after every desired interval. Remove Timer and TimerTask and use the following
val handler = Handler(Looper.getMainLooper())
handler.post(object : Runnable {
override fun run() {
txt1.text = (i+1).toString()
img_Mario.rotation=180f
img_Mario.translationX +=100
img_Mario.translationY +=20
handler.postDelayed(this, 1000)
}
})
Above code is using a handler and posting a task to the UI Thread message queue. The task itself is updating the UI and posting itself again to the UI Thread message queue using the same handler but this time after 1 second delay using the handler.postDelayed() methond
EDIT : How to stop runnable
If you want to stop a specific runnable you can use the following method and pass in the same runnable object that you passed in handler.post(). Surely you have to keep a reference to the runnable at all time to stop it. The above code doesn't keep a reference. See the Complete code below.
handler.removeCallbacks(runnable) //stops a specific runnable
To stop all remaining callbacks or runnable from the UI Thread message queue use this
handler.removeCallbacksAndMessages(null) //stops any pending callback in message queue
Complete code
NOTE: I have added a stop button click listener as an addition
class Actvt_Image<float> : AppCompatActivity() {
private lateinit var handler : Handler
private lateinit var runnable : Runnable // reference to the runnable object
private var i = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_actvt__image)
val pict_mario = findViewById<ImageView>(R.id.img_Mario)
val bt_down = findViewById<Button>(R.id.bt_down)
val bt_stop = findViewById<Button>(R.id.bt_stop)
val frame = findViewById<LinearLayout>(R.id.frame)
val txt1=findViewById<TextView>(R.id.txt1)
handler = Handler(Looper.getMainLooper())
runnable = Runnable {
i++
txt1.text = i.toString()
img_Mario.rotation=180f
img_Mario.translationX +=100
img_Mario.translationY +=20
handler.postDelayed(runnable, 1000)
}
bt_down.setOnClickListener {
handler.post(runnable)
}
bt_stop.setOnClickListener {
//Use this to stop all callbacks
//handler.removeCallbacksAndMessages(null)
handler.removeCallbacks(runnable)
}
}
}
Read more about processes, threads and handler here :
https://developer.android.com/guide/components/processes-and-threads
https://developer.android.com/reference/android/os/Handler
I have one code and it run as I expected
val t = object : Thread() {
override fun run() {
while (!isInterrupted) {
try {
Thread.sleep(1000) //1000ms = 1 sec
runOnUiThread {
i++
txt1.text = i.toString()
img_Mario.rotation=180f
img_Mario.translationX +=20
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
bt_down.setOnClickListener {
i=0
t.start()
}
Could someone show me what is wrong?
I try to use a Handler post a Runnable but it's not execute
var mHandler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mHandler = Handler()
var runnable = Runnable {
Log.d("TEST", "++++ runable")
Log.d("TEST", "++++ come end")
}
Log.d("TEST", "++++ runnable" + runnable)
Log.d("TEST", "++++ handle" + mHandler)
mHandler!!.post { runnable }
}
This is output
09-21 00:56:04.067 4419-4419/? D/TEST: ++++
runnablecom.vpioneer.activity.MainActivity$onCreate$runnable$1#529b8fb4
09-21 00:56:04.067 4419-4419/? D/TEST: ++++ handleHandler
(android.os.Handler) {529b8cb4}
First at all, don't use !! operator, it is a very bad practice (from the doc). With ? you will reach the same behaviour but checking if the instance became null before executing it.
Saying this, using:
mHandler?.post { runnable }
You are actually creating a new lambda containing runnable line. see here below in a more readable way:
mHandler?.post {
runnable
}
This is the equivalent in Java:
mHandler.post(new Runnable(){
public void run(){
runnable;
}
});
To solve this:
Option 1: getting rid of the runnable declaration
mHandler?.post { /*the content of your runnable*/ }
Option 2: using your runnable instance
mHandler?.post(runnable) // normal parentheses
Option 3: crazy way
mHandler?.post { runnable.run() }
Try this code, I hope this is working
Handler().postDelayed({
// You code and delay time
}, 1000L)
You are not starting runnbale. Try this:
mHandler!!.post { runnable.run() }
This is also valid:
mHandler!!.post {
Log.d("TEST", "++++ runable")
Log.d("TEST", "++++ come end")
}