I want to run multiple timers in sequence. When one timer completes the next should start. I thought of using the Handler class however this has the effect of running the timers in parallel. As can be seen by the output below.
Is there a way to make the Timer operation block the thread until its complete or is there a better way to achieve this? Maybe with Futures or Kotlin Coroutines?
I'm new to Android. On iOS I have been able to do this with OperationQueue/Operation(set isAsynchronous = true).
class SequentialTimerTasks {
private val handlerThread: HandlerThread = HandlerThread("HandlerThread")
private lateinit var threadHandler: Handler
class TimerCountTask(private val id: Int) : TimerTask() {
private val TAG = "TimerCountTask"
var count = 0
override fun run() {
Log.d(TAG, "Runnable $id RUNNING TIMER $count")
count++
if (count >=10) {
Log.d(TAG, "Runnable $id CANCEL TIMER $count")
this.cancel()
}
}
}
class RunnableTask(private val id: Int) : Runnable {
private val TAG = "RunnableTask"
override fun run() {
Log.d(TAG, "Runnable $id run() called")
val timer = Timer()
timer.schedule(TimerCountTask(id), 0, 1000)
}
}
fun start() {
handlerThread.start()
threadHandler = Handler(handlerThread.looper)
threadHandler.post(RunnableTask(1))
threadHandler.post(RunnableTask(2))
}
}
OUTPUT
Runnable 1 run() called
Runnable 2 run() called
Runnable 1 RUNNING TIMER 0
Runnable 2 RUNNING TIMER 0
Runnable 2 RUNNING TIMER 1
Runnable 1 RUNNING TIMER 1
Runnable 2 RUNNING TIMER 2
Runnable 1 RUNNING TIMER 2
Runnable 2 RUNNING TIMER 3
Runnable 1 RUNNING TIMER 3
Runnable 2 RUNNING TIMER 4
Runnable 1 RUNNING TIMER 4
Runnable 2 RUNNING TIMER 5
Runnable 1 RUNNING TIMER 5
Runnable 2 RUNNING TIMER 6
Runnable 1 RUNNING TIMER 6
Runnable 2 RUNNING TIMER 7
Runnable 1 RUNNING TIMER 7
Your usage of HandlerThread is superfluous — this class does not do anything useful in your code, and it's Thread is no better than any random Thread you started yourself or the threads created by other libraries. Sure, it has a Looper/Handler, but you are not doing anything useful with Handler to justify it's presence.
Timer is ancient legacy from Java versions prior to Java 5. It's existence predates Android and nobody should use it these days. Citing from Timer's documentation:
Java 5.0 introduced the java.util.concurrent package and one of the concurrency utilities therein is the ScheduledThreadPoolExecutor which is a thread pool for repeatedly executing tasks at a given rate or delay. It is effectively a more versatile replacement for the Timer
The biggest mistake in your code is using 2 separate thread pools: a single-threaded HandlerThread "pool" and a hidden thread pool internally used by Timer. In general you should always minimize usage of multi-threading in your code and isolate "normal" code from multi-threaded artifacts. Using multiple types of threads (other than Android main thread) goes contrary to that purpose.
In theory it is possible to fix your original code to work as intended, but because of the reasons above I suggest to abandon both HandlerThread and Timer and switch to single thread pool, based on ScheduledThreadPoolExecutor:
final int PERIOD = 1000;
final int SECOND_TASK_DEADLINE = 10000;
final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
final Runnable firstTask = new Runnable() {
int count = 0;
public void run() {
count++;
Log.d(TAG, "Runnable 1 RUNNING, timer " + count);
}
};
final Runnable secondTask = new Runnable() {
int count = 0;
public void run() {
count++;
Log.d(TAG, "Runnable 2 RUNNING, timer " + count);
}
};
final Future<?> firstFuture = executorService.scheduleAtFixedRate(firstTask, 0, PERIOD, TimeUnit.MILLISECONDS);
executorService.schedule(new Runnable() {
public void run() {
firstFuture.cancel(false);
executorService.scheduleAtFixedRate(secondTask, 0, PERIOD, TimeUnit.MILLISECONDS);
}
}, SECOND_TASK_DEADLINE, TimeUnit.MILLISECONDS);
Unlike your original code, the code above runs your business logic exclusively on 1 thread — the thread of ScheduledThreadPoolExecutor.
This allows you to take advantage of natural mutual exclusion: despite performing no explicit thread synchronization, your code never races or runs concurrently with itself... because there is only 1 thread.
Related
I want to create a monitoring application and their updated UI every second. for example I have 10 textView for display timing and 10 Progress bar to set some progress to display and 6 timers for display time like a stopwatch. all things in the same activity and its run also at the same time.
But When I used ScheduledExecutorService UI stuck and the application going to not respond. how to Implement all things perfectly without ANR?
Here is My code update textView Timer in the thread
private void getLiveUpdate() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
MyTimerTask myTimerTask = new MyTimerTask(() -> {
runOnUiThread(() -> {
getSetData();
getTime(binding.tvCurrentDate);
setRv_channels(switchGroup1, Utills.switchModels1, binding.rvChannelsMain);
setRv_channels(switchGroup2, Utills.switchModels2, binding.rvChannelsMain1);
setRv_channels(switchGroup3, Utills.switchModels3, binding.rvChannelsMai2);
if (isOtOn) {
binding.tvOtOffTime.setText(timee(otStartCounter));
otStartCounter++;
}
if (isPatientIn) {
binding.tvPOutTime.setText(timee(patientInCounter));
patientInCounter++;
}
if (isSurgIn) {
binding.tvSugOutTime.setText(timee(surgeonTimeCounter));
surgeonTimeCounter++;
}
if (isAnaeIn) {
binding.tvAnafTime.setText(timee(anaeTimeCounter));
anaeTimeCounter++;
}
if (isSurgeryStart) {
binding.tvSurgeryTime.setText(timee(surgeryTimeConunter));
surgeryTimeConunter++;
}
if (isAnaeStart) {
binding.tvAneTime.setText(timee(anaeStartTimeConunter));
anaeStartTimeConunter++;
}
});
});
scheduler.scheduleAtFixedRate(myTimerTask, 0, 1, TimeUnit.SECONDS);
}
private String timee(int seconds) {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, secs);
}
UI thread is one and only, there is no such thing as "multiple UI thread"
if you are performing some actions in separated thread and want to show (partial) results in GUI you have to run GUI-drawing-related code (e.g. textView.setText(...)) in this one and only UI thread. easiest way is to use Handler with MainLooper - its called "main", because UI thread is one and only mandatory working thread, you may not thread your app/code at all. so you can get access to it by some static refrerences, thus below may be pasted literally anywhere in any thread
Handler(Looper.getMainLooper()).post {
// UI related code
}
still if you want to change text in some TextView, which is created/referenced in UI thread only you have to pass reference to it to this "another thread" and use this reference inside Runnable posted for Handler with main Looper
i used a custom thread to fetch data in the background every 1 second by making a thread goes to sleep, but my App crashes and throws exception OutOfMemoryError. Now i read a documentation in android developer and i understand that using custom thread is bad practice as it is difficult to manage the memory consistency. but finally i found ExecutorService very interesting when we need to do some tasks on background So, i decided to use it.
As You know the ExecutorService is like below:
public void executeTask()
{
ExecutorService executroService = new Executor.newSingleThreadExecutor();
executroService.execute(new Runnable()
{
#Override
publi void run()
{
// now execute the server request
}
});
}
Now how can i achive calling to executorService every 1 second untill the user goes to onpause() state or untill the user shifts from the current activity to another activity? if i use a custom thread to call that service, the App goes to crash. so how can i achive that ?
What you need is a ScheduledExecutorService
It can schedule commands to run after a given delay,
or to execute periodically.
Here is a code that implements this
import static java.util.concurrent.TimeUnit.*;
class BeeperControl {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void beepForAnHour() {
Runnable beeper = () -> System.out.println("beep");
ScheduledFuture<?> beeperHandle =
scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
Runnable canceller = () -> beeperHandle.cancel(false);
scheduler.schedule(canceller, 1, HOURS);
}
}
How to repeat same task in Handler Thread in specified time delay and update the view in Main thread also.
I need to run this code in background thread repetitively in a interval of 30 seconds and update the result in Main thread as this will block the UI thread if executed here.
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = mSeconds/3600;
int mins = (mSeconds%3600)/60;
int secs = mSeconds%60;
String timeElapsed = "";
if (hours>0){
timeElapsed = String.format("%02d:%02d",hours,mins);
}else{
timeElapsed = String.format("%02d:%02d",mins,secs);
}
// there are some other networking stuff also which will be executed here
if(mRunning){
mSeconds++;
}
// update the view as well
handler.postDelayed(this, 3000);
}
});
When you want it to execute.
mToastRunnable.run()
Fucntion
private val mToastRunnable = object : Runnable {
override fun run() {
Handler().postDelayed({
// Do Something.
}, 7000) //Delay in the running of this function
mHandler.postDelayed(this, 10000) //Iteration of 10 Sec
}
}
Don't use a Handler for this - it isn't suitable. Without any more information, I'd recommend an ExecutorService:
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
//Run scheduled background task here. Call onto the main thread with
// new Handler(Looper.getMainLooper()).post(() -> { /*task to execute */ });
}, 30, TimeUnit.SECONDS);
RxJava is another good candidate if you want to know how much time has elapsed. This syntax is for RxJava 1.x but it will be very similar for RxJava 2.x (I think you'd just swap Observable for Flowable):
Observable.interval(30, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(interval -> { /* Task. "Interval" is the time elapsed so far */});
I have a textview, and I'm highlighting it dynamically (first 110 letters are highlighted first then after 1 second next 110 letters are highlighted and so on..). Below is my code for it.
I just created background thread as timer, but it is not stopping at all. How do I stop the timer after 3 iterations? Thanks in advance...
int x=0;,y=110//global values
Timer timer = new Timer();
//Create a task which the timer will execute. This should be an implementation of the TimerTask interface.
//I have created an inner class below which fits the bill.
MyTimer mt = new MyTimer();
//We schedule the timer task to run after 1000 ms and continue to run every 1000 ms.
timer.schedule(mt, 1000, 1000);
class MyTimer extends TimerTask {
public void run() {
//This runs in a background thread.
//We cannot call the UI from this thread, so we must call the main UI thread and pass a runnable
if(x==330)
Thread.currentThread().destroy();
runOnUiThread(new Runnable() {
public void run() {
Spannable WordtoSpan = new SpannableString(names[0]);
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), x, y, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
x=x+110;
y=y+110;
textView.setText(WordtoSpan);
}
});
}
}
did you try Handler instead of timer Task?
private static int TIME_OUT = 3000;
//--------------
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// do your task here
}
}, TIME_OUT);
There are some disadvantages of using Timer
It creates only single thread to execute the tasks and if a task takes too long to run, other tasks suffer. It does not handle exceptions thrown by tasks and thread just terminates, which affects other scheduled tasks and they are never run
I have this AsyncTask, that sleeps 0.1 second each time that executes "doInBackground", before that I was using a CountDownTimer to control my time.
My problem is: I want to achieve a timer that can Pause without calling .cancel() and when starts creating another timer.
Is there a way to achieve this is android? I didn't found how to do it in a different way. Can you example it?
Where I've found examples canceling the timer:
How to stop the Timer in android?
Android timer? How-to?
http://www.androidhub4you.com/2013/04/countdown-demo-in-android-custom-timer.html
EDIT
Answering Kevin Krumwiede: This project is a kind of game, that I must hit blocks in a determinated time, so I want to achieve a way to stop the timer when the player use some kind of special power (hit a button specified).
EDIT2
Answering Kushal: I don't know if you have compiled this code, but I can't use the task1 variable. Here goes the code:
public void doIt(){
final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
final Runnable task =
new Runnable() {
#Override
public void run() {
timerAsyncTask.execute();
if(true) {
exec.shutdown(); // shutdown this execution
//exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS);
}
}
};
exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS);
}
Here exec = Executors.newSingleThreadScheduledExecutor(); it shows me an error:
Error:(33, 21) error: cannot assign a value to final variable exec
What I think it's pretty okay, once the exec is a final variable.
And here exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS); I got another error:
Error:(34, 46) error: variable task might not have been initialized
Can you explain, how can I use this piece of code? I'm quite new to android.
Thanks.
I suggest you avoid Timers in Android altogether. Android has a light weight and better solution called Handler
You can find a comparison between these two here. To sum up
Comparison Handler VS Timer
While rescheduling Handler is very easy, you can not reschedule Timer
In Handler you can attach to any Runnable but Timer schedule for only
one TimerTask
TimerTask is purely background task so you can not update
UserInterface, but that's not true for Handler's Runnables
Timer Causes Execptions
Timer tends to leak more memory compare to Handler see the graph of
object retains by timer and Handler. It will increase rapidly for
Timer if you are creating and scheduling new task.
As the post suggests, the Handler usage is pretty simple. Here is an example code
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
public class TestActivity extends AppCompatActivity {
private static int INITIAL_TIMER_DELAY = 100;
private static int TIMER_PERIOD = 100;
private android.os.Handler handler;
private Runnable runnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler(Looper.getMainLooper());
runnable = new Runnable() {
#Override
public void run() {
// Do something and reschedule ourself
handler.postDelayed(this, TIMER_PERIOD);
}
};
handler.postDelayed(runnable, INITIAL_TIMER_DELAY);
}
#Override
protected void onDestroy() {
super.onDestroy();
cancelTimer();
}
private void cancelTimer() {
if (handler != null) {
handler.removeCallbacks(runnable);
}
}
private void rescheduleRunnable() {
handler.postDelayed(runnable, INITIAL_TIMER_DELAY)
}
}
You can achieve your requriment using ScheduledExecutorService class
Basic difference between ScheduledExecutorService and Timer class is :
Using Timer class, you cannot check how your execution is going. You can start and stop execution but cannot check execution based on condition
ScheduledExecutorService provides way to check how execution is running in between start and stop call. If any execution of the task encounters an exception, subsequent executions are suppressed
How we can achieve your requirement :
We need 0.1 second delay between doInBackground() execution
We shall be able to pause our execution when other execution starts
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
public void run() {
// start your AsyncTask here
new <your_task>().execute();
if (<check_your_condition_when_to_pause>) {
exec.shutdown(); // shutdown this execution
exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(task1, 0, 100, TimeUnit.MILLISECONDS);
// here i have passed task1 for example
// here we need to pass next task runnable which
// we want to run after 0.1 seconds
} else {
// continue normal
}
}
};
exec.scheduleAtFixedRate(task1, 0, 100, TimeUnit.MILLISECONDS);
Credit reference : Answer 1 and Answer 2
I hope this will help to solve some of your doubts