How to optimize my code to handle this special event - java

My program listens to 3 types of events: ADD, DELETE, REFRESH which is triggered by a 3rd party library component on UI. My listener looks like this:
//the timestamp is the time when the event occurs
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
//handle ADD event
}else if(event.getName().equals("DELETE")){
//handle DELETE event
}else if(event.getName().equals("REFRESH")){
//handle REFRESH event
}
}
The code works fine for each event, except a little problem with REFRESH event:
when refresh happened on UI, the 3rd party lib component fires consecutive 3 events in a short time, that's: ADD->DELETE->REFRESH, in this case my listener thinks there are 3 events, but actually it is only a refresh action on UI.
How can I optimise my code so that when ADD->DELETE->REFRESH happens consecutively very quickly, my listener could be smart enough to know it is only a REFRESH?
(ADD and DELETE events are NOT instances of the REFRESH event)

As I already said in the comments, the piece of code I posted below is working(tested). You will probably need some tweaking of the REFRESH_TIMEOUT and probably make it thread-safe, but I've tested the basic idea:
"If ADD event comes, create a timer for it. When DELETE comes, check if there is a timertask already. If it's not-> process DELETE. If there is -> process REFRESH. If the timer expires-> process ADD"
It's a bit of a hack, but with the information you gave, I think this solution may be the easiest thing to do. You may get into a problems if the proper events are coming faster then is your REFRESH_TIMEOUT. In that case, the logic will get a bit more complicated.
long REFRESH_TIMEOUT=100;
Timer timer = null;
MyTask myTask = null;
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
timer = new Timer();
timer.schedule(myTask = new MyTask(event), REFRESH_TIMEOUT);
}
if(event.getName().equals("DELETE")){
if (myTask!=null && !myTask.expired){
processRefresh(event);
timer.cancel();
}else{
processDelete(event);
}
}
}
private static class MyTask extends TimerTask {
Event event;
boolean expired;
public MyTask(Event event){
this.event=event;
}
#Override
public void run() {
expired=true;
processAdd(event);
}
}
private void processAdd(Event e){
...
}
private void processDelete(Event e){
...
}
private void processRefrsh(Event e){
...
}

After some thinking, I came up with my own solution:
That's in ADD & DELETE condition, I use Thread.sleep(1000), then get the system time, after which I compare the latest system time get in REFRESH condition, if the difference is within 1sec, then it is a refresh event.
private long timeout = 1000;
private long addEventTime;
private long deleteEventTime;
private long refreshEventTime;
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
Thread.sleep(timeout);
addEventTime = System.currentTimeMillis();
if((refreshEventTime - addEventTime) >timout){
//handle ADD event
}
}else if(event.getName().equals("DELETE")){
Thread.sleep(timeout);
deleteEventTime = System.currentTimeMillis();
if((refreshEventTime - deleteEventTime) >timout){
//handle DELETE event
}
}else if(event.getName().equals("REFRESH")){
refreshEventTime = System.currentTimeMillis();
//handle REFRESH event
}
}
Any guru has any comment on my solution ?

If the event api is well thought imo, I'd think that an ADD event could be an instance of a REFRESH event.
An example of this would be:
//AccidentAPI provided by FooBar Corp.
public CarAccidentEvent extends AccidentEvent {
private String carMake;
public String getMake() {
return carMake;
}
}
So, your listener would be able to do something like this:
public void listenToAccidents(AccidentEvent e) {
if (e instanceOf CarAccidentEvent) {
doStuff();
} else if (e instanceOf SkyJumpingEvent) {
doOtherStuff();
} else {
blah();
}
}
But again, this is going on the assumption that the ADD and DELETE events are instances of the REFRESH event. Though, perhaps their documentation would reveal something further about the EventAPI that may help answer the problem better.
Otherwise, you could add three attributes for the listener for the System's time in Millis which if the time in Millis is a difference greater than say 1ms, then process it, otherwise, go to the REFRESH case.

Related

How to detect multiple clicks and finally fire only one Update in android application

I'm implementing a MediaPlayer for Android at the moment using ContentProviders.
What I want to do is implementing a Listener for the "Next"-button that collects multiple Clicks performed in a specific timeframe and finally firing one Update-Query containing the information of how many tracks to be skipped.
For example in a 250 ms timeframe the button gets clicked 5 times then 5 tracks should be skipped. If it is pressed only once afterwards only one track should be skipped. Also the timeframe should not end after a specific time but be some kind of restarted on every click so that you have the possibility to skip so to speak unlimited amount of tracks and the update will only fire once the timeout is reached AFTER the last clickevent.
I tried multiple implementations but did not succeed to do so until now, and the only things I find in Google is how to AVOID rapid multiple touch events. But that's the point, I intentionally want them to be performed. Any Ideas?
Try this:
class ClickHandler implements Runnable {
private static final long WAIT_DELAY = 250;
private int count = 1;
private long lastSubmitTime = System.currentTimeMillis();
#Override
public void run(){
while(System.currentTimeMillis() - lastSubmitTime <= WAIT_DELAY) {
// idle
Thread.yield();
}
MyActivity.runOnUiThread(new Runnable(){
#Override
public void run(){
// fire event for counter
System.out.println("Count: " + count);
}
});
// reset reference so we can start at 0
handler = null;
}
public void recordNewClick(){
count++;
lastSubmitTime = System.currentTimeMillis();
}
}
Then for your button event handling:
Declare the handler instance for that specific button somewhere:
private ClickHandler handler;
and forward the click-events on that button to your custom handler:
public void onButtonPressed(){
// on input event from your ui component
if(handler == null) {
handler = new ClickHandler();
new Thread(handler).start();
} else {
handler.recordNewClick();
}
}
Note:
You could also avoid creating a new Thread every time, an reuse the existing ClickHandler and just reset it to 0, and wait until at least one click is submitted.
You can increment a counter variable and send an empty message to a Handler.
Every time a click is detected:
increment counter
remove pending messages from the Handler
send an empty message to the Handler with your delay.
Once a message arrives into your handleMessage(Message msg) use the counter value as you wish and after that reset it to zero.

Set a delay in libGDX

I have now tried to set up a delay in libGDX in three different ways.
First I tried to use a Timer, but if I restart the activity, the timer won't start again. This is a known issue when using GestureDetector: https://github.com/libgdx/libgdx/issues/2274
Then I tried setting up a timer using Gdx.graphics.getDeltaTime in my render method, but this doesn't work for me as I have set it to non-continous rendering. Described in answer #2 set a delay in libgdx game
Finally I tried using a while loop and System.getCurrentTimeMilliseconds, however this prevented the application from recognizing a tap while the while loop was looping.
I have also heard of DelayAction, but how does one implement that into the code? https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/actions/DelayAction.html
Is there another way of setting a delay? How do one implement DelayAction? Or how does one fix the Timer bug in libGDX?
Inspired by Barodapride's comment, I found a solution where I make a new thread, and put the while loop here. This code will wait for 1000 ms, and then run foo() on the main thread.
new Thread(new Runnable() {
#Override
public void run() {
long time = System.currentTimeMillis();
while (System.currentTimeMillis() < time + 1000){}
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
foo();
}
});
}
}).start();
Well i just create an interface like this:
public interface TimeEvents {
public void handleTime(float secondsToEvent,Events event, Object obj);
public void resetTimer();
public void handleEvent(Events event,Object obj);
}
and create a class implementing it for the game entity i want, the Events for my case is an enum with the event to process (Like wait for x seconds, walk for x seconds, fire for x seconds..), the object is the instance of target object i want to handle by event..
public class FooTimeEvents implements TimeEvents{
[...]
private float timeSeconds = 0;
#Override
public void handleTime(float secondsToEvent,Events event, Object obj){
timeSeconds +=Gdx.graphics.getRawDeltaTime();
if(timeSeconds > secondsToEvent){
timeSeconds-=secondsToEvent;
handleEvent(event,obj);
}
}
#Override
public void handleEvent(Events event,Object obj){
switch (event) {
case EVENT_FOO_1:
executeEventFoo1((Foo1Obj)obj);
break;
case EVENT_FOO_2:
executeEventFoo2((Foo2Obj)obj);
break;
default:
break;
}
}
[...]
you call the handleTime on render method of the entity, and the event will only execute each timeSeconds it was set to..
You can use a RunnableAction with a DelayAction. Here are the steps you would take:
Instantiate a RunnableAction and provide it with a Runnable:
RunnableAction runnableAction = Actions.run(new Runnable(){
public void run(){
// Put whatever you want to do here
}
}
Instantiate a DelayAction and provide it with your RunnableAction:
DelayAction delayAction = Actions.delay(secondsOfDelay, runnableAction);
Now in your render or update method you need to tell your delayAction to 'act':
delayAction.act(delta);
Once the delay has passed the code in your runnable should run. I'm sorry if this syntax isn't exactly correct but this should be the easiest way to go. Let me know if something doesn't work for you and I can help.

How to use FutureTask<V>, waiting for ui event?

I have an interface method which is supposed to return a Future object.
Future<Result> doSomething()
The implementation of this method shows some ui (javafx).
One of the ui elements has a listener, that needs to be called in order to receive the actual result, I need.
How do I achieve this?
Is there a better solution?
Here an example action I need to wait for:
// this is some framework method I cannot change
#Override
public Data execute(Data data) {
Future<Data> dataFuture = handler.doSomething(data);
// this should basically wait until the user clicked a button
return dataFuture.get();
}
// handler implementation
public Future<Data> doSomething(Data data) {
// the question is how to implement this part, to be able to
// return a future object
Button button = new Button("Wait until click");
// create thread that waits for the button click ?!????
// modify incoming data object when the button was clicked
// somehow create the Future object that's bound to the button click
return future;
}
This is what I want to achieve:
my method doSomething shows a new scene(ui) with a button on it
and returns immedeately the future object
future.get() waits until the user pressed the button
limitations: it has to be done with no extra library and on >=Java7
Use a javafx.concurrent.Task. It derives from FutureTask. There are extensive examples in the linked javadoc on Task usage.
Oracle also provide a tutorial which discusses Task usage:
Concurrency in JavaFX
I think this is what you want, but I may have understood the question, if so, please edit the question a bit to clarify requirements (perhaps with an mcve). The bit that makes me a little unsure is the part in your title "waiting for ui event?", I'm not quite sure what that means in this context.
This is a solution I was searching for. It's not very nice, since the Thread.sleep doesn't convince me.
but now you propably get an idea of what I want to achieve
// make sure this is not called on the ui thread
public Future<Data> doSomething(Data data) {
WaitingFuture future = new WaitingFuture(data);
Platform.runLater(() -> {
Button button = new Button("Wait until click");
button.setOnAction(future);
// show button on ui...
});
favouriteExecutorService.submit(future);
return future;
}
static class WaitingFuture extends Task<Data> implements EventHandler<ActionEvent> {
private Data data;
WaitingFuture(Data originalData) {
this.data = originalData;
}
private Data waitingData;
#Override
public void handle(ActionEvent event) {
waitingData = data.modify();
}
#Override
protected Data call() throws Exception {
while (waitingData == null) {
Thread.sleep(100);
}
return waitingData;
}
}

SwingWorker done method throws cancellationexception with get()

I faced an issue of creating stop/start jbuttons for my gui, and after a LOT of googling, i realized i needed multi-threading. Upon further reading i discovered the swingworker class, and i managed to get my GUI to respond to the STOP button.
now my problem is this
The doinbackground() method executes a piece of code that captures packets in an infinite while loop with the condition (!isCancelled), and once it is cancelled (The STOP button executes worker.cancel()) it returns an ArrayList of packets which theoretically, i should be able to obtain inside the done() method using get(). right? But when i try to do this i get a CancellationException and this is driving me nuts right now.
any help would be highly appreaciated!
Thank you
edit: obj is an ArrayList declared outside of the class to store the return values.
here is my code executed by the START jbutton
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
final ArrayList packet_list = new ArrayList();
obj.clear();
try {
worker = new SwingWorker<ArrayList,Integer>(){//initialze swingworker class
#Override
protected void done(){
try {
obj = get();
}
catch (InterruptedException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
//opens up stuff required to capture the packets
NetworkInterface [] devices = JpcapCaptor.getDeviceList();
int index = (jComboBox5.getSelectedIndex()-1);
JpcapCaptor captor =JpcapCaptor.openDevice(devices[4], 65535, false, 20);
#Override
protected ArrayList doInBackground(){
while(!isCancelled()){
try {
Packet packets = captor.getPacket(); //captures packets
if (packets != null) //filters out null packets
{
//System.out.println(packets);
packet_list.add(packets); //adds each packet to ArrayList
}
Thread.sleep(100);
} catch (InterruptedException ex) {
return packet_list;
}
}
return packet_list;
}
};
worker.execute();
} catch (IOException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
The stop button simply executes
worker.cancel(); no errors there. and this is the swingworker declaration
private SwingWorker<ArrayList,Integer> worker;
cancel doesn't just set the isCancelled flag for you to read at your leisure. That would be pretty much useless. It prevents the task from starting if it hasn't already and may actively interrupt the thread if it's already running. As such, getting a CancellationException is the natural consequence of cancelling a running task.
To further the point, the Javadoc on isCancelled states:
Returns true if this task was cancelled before it completed normally.
Hence if this returns true, then your task cannot complete normally. You cannot cancel a task and expect it to continue as per normal.
SwingWorker docs say "An abstract class to perform lengthy GUI-interaction tasks in a background thread". However, the definition of "lengthly" is different for GUI and for an application lifetime. A 100ms task is very long for a GUI, and is best done by a SwingWorker. A 10 minute task is too long for a SwingWorker simply because it has a limited thread pool, that you may exhaust. Judging by your problem description, you have exactly that - a potentially very long running task. As such, you should rather make a proper background thread than use a SwingWorker.
In that thread, you would have either an AtomicBoolean or simply a volatile boolean flag that you can manually set from the EDT. The thread can then post an event to the EDT with the result.
Code:
class PacketCaptureWorker implements Runnable {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
public void run() {
while (!cancelled) {
//do work
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Use the result of your computation on the EDT
}
});
}
}
new Thread(new PacketCaptureWorker()).start();
I tried using a volatile boolean instead of using worker.cancel() for the swingworker thread while loop and it works beautifully. (atleast on surface) I managed to create a normal background thread as well and that too worked liked a charm :D Many thanks you saved me a major headache! Wondering what the best method is out of the two.
A follow up, i had to make the volatile boolean available for the whole class, because i had to create 2 seperate instances for the thread class, one to use the START and the other to use the STOP. Apparently two different instances does not address the same instance of the variable. is this bad practice?

Delaying SWT Table Refresh

We have a ViewerFilter for a TableViewer that is a little slow, so to try to give the impression of awesomeness, we wanted to have the viewer wait 500 milliseconds before refreshing the window (otherwise, it was blocking after every key stroke).
Not having any clue what I was doing, I tried creating a class that would check if System.currentTimeMillis() was greater then the time of the last key stroke + 500 from a different thread. This just caused an Invalid thread access exception to be thrown, so I'm lost.
Edit: I was able to use TableViewer.getTable().getDisplay().asyncExec() to sidestep the invalid thread problem, but I don't like my solution, and would love to hear other suggestions.
You might want to try to turn off redraw while updating the viewer.
Viewer.getControl().setRedraw(false);
// update
Viewer.getControl().setRedraw(true);
It can sometimes give a better user experience. You can also schedule a ui-job that you cancel when the user hits a new key or modifies the text. E.g.
class RefreshJob extends WorkbenchJob
{
public RefreshJob()
{
super("Refresh Job");
setSystem(true); // set to false to show progress to user
}
public IStatus runInUIThread(IProgressMonitor monitor)
{
monitor.beginTask("Refreshing", ProgressMonitor.UNKNOWN);
m_viewer.refresh();
monitor.done();
return Status.OK_STATUS;
};
}
and then reschedule the refresh in a separate job.
private RefreshJob m_refreshJob = new RefreshJob();
private Text m_filterText;
void hookModifyListener()
{
m_filterText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
m_refreshJob.cancel();
m_refreshJob.schedule(500);
}
});
}
If the user hits the Enter key you can schedule a refresh job without the delay,
Just wrap your code in display.syncExec, something like this:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// check refresh time
// refresh.
}
});
You may want to look in to asyncExec too, if syncExec does not meet your needs.

Categories