Набор с клавиатуры: выполнить запрос с задержкой между символами

Чтобы уменьшить нагрузку на сервер при поиске, я предусмотрю задержку ввода с клавиатуры.

вариант использования:
1. пользователь вводит символы
2. если задержка между вводом символов ‹ 1 секунда, поиск НЕ выполняется немедленно и ожидает, когда задержка после ввода последнего символа составляет > 1 секунду
3. если задержка между вводом символов > 1 секунда этот поиск выполняется немедленно

Есть ли лучшие практики в JSE или в мобильной Java (Blackberry)?

Похоже, мне следует использовать TimerTask и API Timer для таких случаев. Это?


person sergionni    schedule 15.04.2012    source источник


Ответы (2)


Согласно библии параллелизма Java 'параллелизм Java на практике'

... Таймер имеет некоторые недостатки, и ScheduledThreadPoolExecutor следует рассматривать как его замену.

Таким образом, я бы сделал что-то вроде этого примера:

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);
    }
}

Примечание. Я обновляю статус из темы, отличной от EDT, что является незаконным, но вы поняли идею.

РЕДАКТИРОВАТЬ: на основе замечательных комментариев ниже (спасибо!) Timer будет работать в этом простом случае, и это упрощает использование потока демона (хотя у него есть проблемы с задачами, которые генерируют исключения, как описано в книге). Для этого замените указанные выше исполнителя и слушателя следующим образом:

        ...
        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);
            }
        });
person Ian Jones    schedule 15.04.2012
comment
Ян, спасибо за подсказку, я дам вам обратную связь, как только попробую. - person sergionni; 15.04.2012
comment
По моему опыту работы, Timer API по-прежнему является лучшим выбором для тривиального использования. Executors API сложен, и в действительности он не может предложить что-то большее, чем простой сценарий. В частности, если вы хотите, чтобы ваш поток таймера был потоком демона (всегда хорошая идея для таких простых задач — явная очистка не требуется), инфраструктура Executors заставляет вас создать для этого собственный ThreadFactory. - person Marko Topolnik; 15.04.2012
comment
@Marko, это хорошее замечание о потоках демонов. У меня уже есть DaemonThreadFactory в моем проекте, который я использую в разных местах, поэтому не вижу в этом больших накладных расходов. Проблема с Timer даже в этом простом примере заключается в том, что вы не можете отменить задачу, вы можете отменить только весь Timer, то есть вам каждый раз нужна новая задача (и новый поток). - person Ian Jones; 15.04.2012
comment
@IanJones Но TimerTask может быть отменено. В этом отношении это почти то же самое, что и Future. - person Marko Topolnik; 15.04.2012
comment
@MarkoTopolnik, вы абсолютно правы, приношу свои извинения. Я обновлю свой ответ. - person Ian Jones; 15.04.2012
comment
вопрос по поводу состояния:if(task != null)task.cancel();как я понимаю всегда будет null на тот момент, не так ли? - person sergionni; 16.04.2012
comment
о, я понял)) Просто игнорируйте предыдущий комментарий, пожалуйста. - person sergionni; 16.04.2012

Да, используйте TimerTask. Я бы предложил дополнительное изменение ваших правил: просто убедитесь, что запросы разнесены не менее чем на секунду, но не ждите, пока пользователь перестанет печатать на секунду. Вместо этого сразу после истечения тайм-аута отправьте новый запрос с состоянием поля ввода на тот момент. Пользователь может быть в процессе набора текста, но его взаимодействие с вашим приложением будет более плавным и отзывчивым.

person Marko Topolnik    schedule 15.04.2012