Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Lightweight postDelayed / removeCallbacks

Reply
Thread Tools

Lightweight postDelayed / removeCallbacks

 
 
Jan Burse
Guest
Posts: n/a
 
      02-04-2012
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/referen...view/View.html


 
Reply With Quote
 
 
 
 
Steven Simpson
Guest
Posts: n/a
 
      02-05-2012
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

 
Reply With Quote
 
 
 
 
Jan Burse
Guest
Posts: n/a
 
      02-06-2012
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 --------------------



 
Reply With Quote
 
Jan Burse
Guest
Posts: n/a
 
      02-06-2012
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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Where lightweight cheap JMS to MSMQ Bridges ? Jules Java 5 09-09-2009 05:25 PM
lightweight server framework Chorg Java 2 01-24-2004 12:41 AM
Any interest in lightweight coroutines in Java ala C# 2.0 iterators? Ken Sprague Java 4 10-28-2003 08:03 PM
When I spawn a lightweight process in ASP.NET Does it time out?? MS News ASP .Net 2 07-31-2003 11:53 PM
lightweight XSLT-based page flow: anyone thought of this? SUPER KOOL 223 Java 0 07-29-2003 08:01 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57