The Timer and TimerTask classes are suitable for straightforward periodic tasks:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Overridepublicvoidrun(){
System.out.println("All work and no play makes Jack a dull developer!"); // The Shining, dev style. }
}, 0, 1000); // Start ASAP, repeat every 1000ms (1 second).
Invoke the run method of TimerTask every second without delay. Bring it to a halt with cancelwhen necessary.
Welcome the Executors
For handling complex tasks like database connections, using ExecutorService and Future introduces more control and reliability:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Callable<String> task = () -> {
// Mystery: Why do developers don't talk to DB directly? They're afraid to drop a table. return"Successful connection";
};
Future<String> future = executor.schedule(task, 2, TimeUnit.MINUTES);
try {
System.out.println(future.get(120, TimeUnit.SECONDS)); // Waits 2 mins, longer than my coffee break.} catch (InterruptedException | ExecutionException | TimeoutException e) {
// If anything goes wrong, we panic... properly! e.printStackTrace();
} finally {
// Don't leave your threads running, *they need some sleep too*. executor.shutdown();
}
Prepared for rain: Handling timeouts and exceptions
As we sail in the sea of code, it's crucial to be ready for the storms of timeouts and exceptions:
Future<String> future = executor.schedule(task, 1, TimeUnit.SECONDS);
try {
System.out.println(future.get(1, TimeUnit.SECONDS)); // Wait for 1s, done by the time you blink.} catch (InterruptedException | ExecutionException e) {
e.printStackTrace(); // Log and scream!} catch (TimeoutException e) {
e.printStackTrace(); // Log and scream, but in a hurry!} finally {
executor.shutdown(); // Always close the door when you leave.}
The scheduler ultimatum
Grasping the difference between scheduling methods can save your task:
scheduleAtFixedRate: Consistent frequency, regardless of execution time.
schedule: Ensures delay between task executions, considering execution time.
Make your pick based on whether you need a regular interval or a fixed delay.
Handling DB tasks with Callable and FutureTask
When you're dealing with data connectivity tasks, Callable and FutureTask offer more control and clarity:
Callable<Boolean> callable = () -> {
boolean successful = db.connect(); // Are we there yet?return successful;
};
FutureTask<Boolean> task = new FutureTask<>(callable);
executor.execute(task);
try {
Boolean result = task.get(2, TimeUnit.MINUTES); // Wait! The DB is shy. Give it 2 minutes.if (result) {
System.out.println("Database is friendly!");
}
} catch (TimeoutException e) {
System.out.println("The DB ghosted us. Time out!"); // Rejected by DB. Ouch!} finally {
executor.shutdownNow();
}
Always cater to InterruptedException and ExecutionException. Moreover, avoid infinite task runs because resources aren't infinite.
Android UI tasks? We got this
In an Android world, the UI-related tasks have to be on the main thread:
Activity activity = ... // Your cool activitytimer.schedule(new TimerTask() {
@Overridepublicvoidrun(){
activity.runOnUiThread(new Runnable() {
@Overridepublicvoidrun(){
// The UI magician is here! }
});
}
}, 0, 1000);
This makes sure you avoid the infamous CalledFromWrongThreadException. Just remember, Thread carefully here.