Reputation: 833
I am fairly new to Java; (<= lol I just typed semicolon instead of dot (after 2 days of non-stop coding. Leaving it there so that everyone can laugh as well :D )
I wasn't sure what I was looking for, so the title is not very helpful.
Best I can describe is that things seems to run parallel. I am not sure how to explain this, so I'll show you an example. I have a timer class that waits for a duration each loop.
If I run the following method(not in the timer class, but just another one), it doesn't wait until the Runnable part(Timer) is finished like for loop would do. It computes the code below at the same time.
How can I make the program to wait for the Timer to finish? Also, why is it running parallel?
I trimmed most of the unnecessary codes but please ask if you need the whole code.
t = Timer;
public void turnend() {
if (leftover == 0) {
housenumber = 1;
Runnable r = new Runnable() {
@Override
public void run() {
//code1
if (housenumber == 8) {
t.stopTimer();
}
}
};
t = new Timer(r, 500, true);
}
//code 2
}
//code 1 and 2 runs at the same time
EDIT: Timer class
import android.os.Handler;
public class Timer {
private Handler handler;
private boolean paused;
private int interval;
private Runnable task = new Runnable () {
@Override
public void run() {
if (!paused) {
runnable.run ();
Timer.this.handler.postDelayed (this, interval);
}
}
};
private Runnable runnable;
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public void startTimer () {
paused = false;
handler.postDelayed (task, interval);
}
public void stopTimer () {
paused = true;
}
public Timer (Runnable runnable, int interval, boolean started) {
handler = new Handler ();
this.runnable = runnable;
this.interval = interval;
if (started)
startTimer ();
}
}
Upvotes: 1
Views: 325
Reputation: 63955
Android's main thread looks a bit like
while (true) {
Runnable nextThingToDo = getNextTaskFromQueue();
nextThingToDo.run();
}
and Handler
s are used to put a new task into the queue. All the code you write in on????
methods like Activity#onCreate
is also part of such tasks. Things like touch event handling & drawing updates of the screen appear in that queue too. You must therefore never pause that thread, the only way to do things "later" is to enqueue it for later (the queue is not a simple first-in-first-out queue, it supports delays and such)
Now, your Timer
schedules the task
Runnable
to run after a set delay. Since the code that calls handler.postDelayed
is necessarily already inside such a runnable itself it needs to be done before the queue can take and run task
. So what happens in the unrolled while loop is roughly
Runnable nextThingToDo = getNextTaskFromQueue();
nextThingToDo.run();
// inside above "run"... whatever code path leads to turnend()
if (leftover == 0) {
housenumber = 1;
Runnable r = construct();// it's only a reference to a `Runnable` object, nothing executes here
t = new Timer(r, 500, true);
// inside Timer
handler.postDelayed(task, 500); // task is now in the queue
}
//code 2 -- it actually runs now.
// whatever else is in your code on the way out from "turnend()"
// ... things happen, 500ms passes
Runnable nextThingToDo = getNextTaskFromQueue(); // this is the `task` object
nextThingToDo.run();
// inside this "run"
if (!paused) {
runnable.run (); // this is the `r` object
// inside task
{
// code1 -- running ~ 500ms after code2
if (housenumber == 8) {
t.stopTimer();
}
}
To solve your problem, you should first of all understand that writing code that happens in multiple steps over time is not straight & simple and it needs to be written so that code always triggers future code, a neverending series of events. Code can not wait on future tasks to complete, you can only tell the future code what you want to do once it's done.
For example by moving the code physically into the place where it belongs:
public void turnend() {
if (leftover == 0) {
housenumber = 1;
Runnable r = new Runnable() {
@Override
public void run() {
//code1
if (housenumber == 8) {
t.stopTimer();
}
//code 2
}
};
t = new Timer(r, 500, true);
}
}
Or by providing the future code with a reference to code you want to be called, this is what is called a "callback". In it's simplest form:
public void turnend() {
final Runnable callback = new Runnable() {
@Override
public void run() {
//code 2
}
};
if (leftover == 0) {
housenumber = 1;
Runnable r = new Runnable() {
@Override
public void run() {
//code1
if (housenumber == 8) {
t.stopTimer();
}
callback.run();
}
};
t = new Timer(r, 500, true);
}
}
Everytime you want to write
public void things() {
// code 1
// delay of 500
// code 2
}
Instead do roughly:
public void thingsPart1() {
// code1
handler.postDelayed(new Runnable() {
@Override
public void run() {
thingsPart2();
}
}, 500);
}
public void thingsPart2() {
// code 2
}
Upvotes: 1
Reputation: 3409
If you want to block the current Thread, you should use Thread.sleep(500) static method instead of a Timer.
Upvotes: 1