2

In order to decrease server overload while searching,I'm going to provide delay for keyboard input.

use case:
1. user types symbols
2. if delay between typing symbols < 1 second,that search is NOT performed immediately and waiting when delay after last typed symbol is > 1second
3. if delay between typing symbols > 1 second,that search performed immediately

Is there best practises in JSE or in mobile Java(Blackberry)?

Looks like I should use TimerTask and Timer APIs for such case.Is it?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
sergionni
  • 13,290
  • 42
  • 132
  • 189

2 Answers2

2

According to Java concurrency bible 'Java concurrency in practice'

... Timer has some drawbacks, and ScheduledThreadPoolExecutor should be thought of as its replacement.

Thus, I would do something like this example:

public class DelayedSearch extends JFrame {
    public DelayedSearch() {
        final JPanel panel = new JPanel(new BorderLayout());
        final JTextField field = new JTextField(30);
        panel.add(field, BorderLayout.NORTH);
        final JLabel status = new JLabel(" ");
        panel.add(status, BorderLayout.SOUTH);
        this.add(panel);
        this.pack();
        final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        field.addKeyListener(new KeyAdapter() {
            private ScheduledFuture<?> scheduled;
            @Override
            public void keyTyped(KeyEvent e) {
                if (scheduled != null) scheduled.cancel(false);
                scheduled = executor.schedule(new Runnable() {
                    @Override
                    public void run() { // Perform search here. Just set status for demo.
                        status.setText("Search: " + field.getText());
                    }
                }, 1, TimeUnit.SECONDS);
            }
        });
    }
    public static void main(String[] args) {
        new DelayedSearch().setVisible(true);
    }
}

Note: I'm updating the status from a thread other than the EDT here which is illegal, but you get the idea.

EDIT: based upon great comments below (thanks!) Timer will work in this simple case, and it makes it easy to use a daemon thread (although it does have problems with tasks that throw exceptions, as described in the book). To do this replace above executor and listener as follows:

        ...
        final Timer timer = new Timer(true); // use daemon thread.
        field.addKeyListener(new KeyAdapter() {
            private TimerTask task;
            @Override
            public void keyTyped(KeyEvent e) {
                if(task != null)task.cancel();
                task = new TimerTask() {
                    @Override
                    public void run() {
                        status.setText("Search: " + field.getText());
                    }
                };
                timer.schedule(task, 1000);
            }
        });
Ian Jones
  • 1,998
  • 1
  • 17
  • 17
  • Ian, thank you for hint,I'll give you feedback as soon try it – sergionni Apr 15 '12 at 17:10
  • In my work experience the `Timer` API is still the better choice for trivial usage. the `Executors` API is complex and it doesn't really have much more to offer to a simple scenario. Especially, if you want your timer thread to be a daemon thread (always a good idea for such simple tasks -- no explicit cleanup required), the `Executors` framework forces you to create a custom `ThreadFactory` for that. – Marko Topolnik Apr 15 '12 at 17:18
  • @Marko its a good point about daemon threads. I already have a [DaemonThreadFactory](http://stackoverflow.com/a/1517264/456956) in my project that I use in various places so don't see this as much of an overhead. The problem with `Timer` even in this simple example is that you can't cancel a task, you can only cancel the whole `Timer`, meaning you need a new one (and a new thread) each time. – Ian Jones Apr 15 '12 at 19:23
  • 1
    @IanJones But `TimerTask` *can* be [canceled](http://docs.oracle.com/javase/1.4.2/docs/api/java/util/TimerTask.html#cancel()). It's pretty much thet same as a `Future` in that respect. – Marko Topolnik Apr 15 '12 at 19:28
  • 1
    @MarkoTopolnik you are absolutely correct, my apologies. I will update my answer. – Ian Jones Apr 15 '12 at 19:53
  • question about condition:`if(task != null)task.cancel();`as I understand it always be `null` on that point,isn't it? – sergionni Apr 16 '12 at 17:22
  • oh,I've got it)) Just ignore previous comment,please. – sergionni Apr 16 '12 at 17:38
1

Yes, use a TimerTask. I would suggest an additional change to your rules: just make sure the requests are spaced at least a second, but don't wait till after the user stops typing for a second. Istead, immediately when the timout expires issue a new request with the input field state at that moment. The user may be in the middle of typing, but his experience with your app will be more fluid and responsive.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436