Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.java.programmer > #11740 > unrolled thread
| Started by | Jan Burse <janburse@fastmail.fm> |
|---|---|
| First post | 2012-02-04 20:44 +0100 |
| Last post | 2012-02-06 01:35 +0100 |
| Articles | 4 — 2 participants |
Back to article view | Back to comp.lang.java.programmer
Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-04 20:44 +0100
Re: Lightweight postDelayed / removeCallbacks Steven Simpson <ss@domain.invalid> - 2012-02-05 22:00 +0000
Re: Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-06 01:15 +0100
Re: Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-06 01:35 +0100
| From | Jan Burse <janburse@fastmail.fm> |
|---|---|
| Date | 2012-02-04 20:44 +0100 |
| Subject | Lightweight postDelayed / removeCallbacks |
| Message-ID | <jgk1q9$9o1$1@news.albasani.net> |
Dear All,
Just fidling around with a Android / Swing port.
Just wonder whether somebody has worked on an
untility to provide the following under Swing
which is inspired by the Android View(*):
postDelayed(Runnable r, int d):
Posts an event on the EDT, which will
invoked r after a delay of d millisecond.
removeCallbacks(Runnable r):
Immediately remove all events from the
EDT that would invoke r.
One could use javax.swing.Timer for the first
method. Something along:
public void postDelayed(final Runnable r, int d) {
final Timer t=new Timer(d,new ActionListener() {
public void actionPerformed(ActionEvent e) {
r.run();
}
});
t.setRepeats(false);
t.start();
}
But then for the second method one would need to
track the created timers, so as to be able to
selectively stop them.
Anybody already implemented a corresponding utility
in the spirit of SwingUtil.invokeLater/invokeAndWait?
Would be glad to receive hint so as not to reinvent
the wheel.
Bye
(*)
http://developer.android.com/reference/android/view/View.html
[toc] | [next] | [standalone]
| From | Steven Simpson <ss@domain.invalid> |
|---|---|
| Date | 2012-02-05 22:00 +0000 |
| Message-ID | <ipa309-7pn.ln1@news.simpsonst.f2s.com> |
| In reply to | #11740 |
On 04/02/12 19:44, Jan Burse wrote:
> postDelayed(Runnable r, int d):
> Posts an event on the EDT, which will
> invoked r after a delay of d millisecond.
>
> removeCallbacks(Runnable r):
> Immediately remove all events from the
> EDT that would invoke r.
>
> One could use javax.swing.Timer for the first
> method. Something along:
>
> public void postDelayed(final Runnable r, int d) {
> final Timer t=new Timer(d,new ActionListener() {
> public void actionPerformed(ActionEvent e) {
> r.run();
> }
> });
> t.setRepeats(false);
> t.start();
> }
>
> But then for the second method one would need to
> track the created timers, so as to be able to
> selectively stop them.
A first stab:
Keep a WeakHashMap<Runnable,Collection<Reference<Timer>>>, making all
changes on the EDT. Store weak references to each created Timer, in a
HashSet per Runnable.
When asked to remove all callbacks, remove the Runnable from the map,
get its Collection of Timers, and stop them. If the references have
already expired, you don't care. When all the Timers using the same
Runnable have expired, assuming that they naturally decay, the map entry
will be removed anyway.
// uncompiled
void removeCallbacks(final Runnable r) {
invokeAndWait(new Runnable() {
public void run() {
Collection<Reference<Timer>> timers = map.remove(r);
if (timers == null) return;
for (Reference<Timer> rt : timers) {
Timer t = rt.get();
if (t != null) t.stop();
}
}
});
}
void postDelayed(final Runnable r, int d) {
final Timer t=new Timer(d,new ActionListener() {
public void actionPerformed(ActionEvent e) {
r.run();
}
});
invokeAndWait(new Runnable() {
public void run() {
Collection<Reference<Timer>> coll = map.get(r);
if (coll == null) {
coll = new HashSet<Reference<Timer>>();
map.put(r, coll);
}
coll.add(new WeakReference<Timer>(t));
}
});
// should use d too!
t.setRepeats(false);
t.start();
}
--
ss at comp dot lancs dot ac dot uk
[toc] | [prev] | [next] | [standalone]
| From | Jan Burse <janburse@fastmail.fm> |
|---|---|
| Date | 2012-02-06 01:15 +0100 |
| Message-ID | <jgn62j$4de$1@news.albasani.net> |
| In reply to | #11750 |
Steven Simpson schrieb:
> A first stab:
Thank you very much for your solution. Since I
was in need of a solution I implemented already
something yesterday.
Difference to your stab: postDelayed() and
removeCallbacks() do not wait for mouse /
keyevents / etc.. to flush. The problem
is that invokeAndWait() calls postEvent(),
which calls flushPendingEvents() in turn.
The Android removeCallbacks() and postDelayed()
are also immediate. They call in turn
removeMessages() respectively enqueueMessage()
which are immediate.
I didn't use javax.swing.Timer since I still
think they are not leightweight enough. So
my waiting is done via Object.wait(), whereas
the Swing timer uses Condition.awaitNanos().
Not sure what is more reliable.
I also use System.currentTimeMillis() whereas
the Swing timer uses System.nanoTime(). nanoTime()
is more stable, it does not change when the
wall clock is changed on the computer.
But current solution is then to use tail
recursion for a looping animation. Which
can get jerky, since the rate will be not
fixed. The delivery of the event might still
take some time before a next postDelayed()
happens, since it is delivered via postEvent()
which uses flushPendingEvents(). But this is
tollerated in the current application.
Could use a post with runnable = null to stop
the thread, something similar is done in the
Android looper. But currently the thread has to
be started via start(), and can then be easily
terminated via interrupt().
---------------- Begin Code ------------------
public class ThreadTimer extends Thread {
private final ArrayList<TimerEntry> entrylist =
new ArrayList<TimerEntry>();
private final Object entrylock = new Object();
public void postDelayed(Runnable r, int d) {
TimerEntry entry = new TimerEntry(r,
System.currentTimeMillis() + d);
synchronized (entrylock) {
for (int i = 0; i < entrylist.size(); i++) {
TimerEntry entry2 = entrylist.get(i);
if (entry.getWhen() < entry2.getWhen()) {
entrylist.add(i, entry);
entrylock.notifyAll();
return;
}
}
entrylist.add(entry);
entrylock.notifyAll();
}
}
public void removeCallbacks(Runnable r) {
synchronized (entrylock) {
int backsize = entrylist.size();
for (int i = entrylist.size() - 1; i >= 0; i--) {
TimerEntry entry = entrylist.get(i);
if (entry.getRunnable() == r)
entrylist.remove(i);
}
if (backsize != entrylist.size())
entrylock.notifyAll();
}
}
private TimerEntry take() throws InterruptedException {
for (; ; ) {
synchronized (entrylock) {
if (entrylist.size() == 0) {
entrylock.wait();
} else {
TimerEntry entry = entrylist.get(0);
long now = System.currentTimeMillis();
if (entry.getWhen() <= now) {
entrylist.remove(0);
return entry;
} else {
entrylock.wait(entry.getWhen() - now);
}
}
}
}
}
public void run() {
try {
for (; ; ) {
TimerEntry entry = take();
SwingUtilities.invokeLater(entry.getRunnable());
}
} catch (InterruptedException x) {
/* */
}
}
}
class TimerEntry {
private final Runnable runnable;
private final long when;
TimerEntry(Runnable r, long w) {
runnable = r;
when = w;
}
Runnable getRunnable() {
return runnable;
}
long getWhen() {
return when;
}
}
---------------- End Code --------------------
[toc] | [prev] | [next] | [standalone]
| From | Jan Burse <janburse@fastmail.fm> |
|---|---|
| Date | 2012-02-06 01:35 +0100 |
| Message-ID | <jgn789$6d6$1@news.albasani.net> |
| In reply to | #11751 |
Jan Burse schrieb:
> ---------------- Begin Code ------------------
Correction:
private TimerEntry take() throws InterruptedException {
synchronized (entrylock) {
for (; ; ) {
Since wait() and wait(int) anyway kind of
release the lock. See also the DelayQueue
implementation in java.util.concurrent.
Bye
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.java.programmer
csiph-web