Статьи

CallSerially EDT & InvokeAndBlock (часть 2)

834427872
В прошлый раз, когда мы говорили о EDT, мы рассмотрели некоторые основные идеи, такие как последовательный вызов и т. Д. Мы оставили два основных понятия, которые несколько более продвинуты.

Вызывать и блокировать

Когда мы пишем типичный код на Java, нам нравится, чтобы этот код был в такой последовательности:

1
2
3
doOperationA();
doOperationB();
doOperationC();

Это нормально работает, но на EDT это может быть проблемой, если одна из операций медленная, она может замедлить весь EDT (рисование, обработка событий и т. Д.). Обычно мы можем просто переместить операции в отдельный поток, например:

1
2
3
4
5
6
7
doOperationA();
new Thread() {
    public void run() {
         doOperationB();
    }
}).start();
doOperationC();

К сожалению, это означает, что операция C будет происходить параллельно с операцией C, что может быть проблемой… Например, вместо использования имен операций позволяет использовать более «реальный» пример:

1
2
3
updateUIToLoadingStatus();
readAndParseFile();
updateUIWithContentOfFile();

Обратите внимание, что первая и последняя операции должны выполняться на EDT, но средняя операция может быть очень медленной!
Так как updateUIWithContentOfFile требует readAndParseFile, чтобы перед тем, как он это сделает, нового потока будет недостаточно. Наш автоматический подход — сделать что-то вроде этого:

1
2
3
4
5
6
7
updateUIToLoadingStatus();
new Thread() {
    public void run() {
          readAndParseFile();
          updateUIWithContentOfFile();
    }
}).start();

Но updateUIWithContentOfFile должен выполняться в EDT, а не в случайном потоке. Таким образом, правильный способ сделать это будет примерно так:

01
02
03
04
05
06
07
08
09
10
11
updateUIToLoadingStatus();
new Thread() {
    public void run() {
          readAndParseFile();
          Display.getInstance().callSerially(new Runnable() {
               public void run() {
                     updateUIWithContentOfFile();
               }
          });
    }
}).start();

Это совершенно законно и будет работать достаточно хорошо, однако усложняется, поскольку мы добавляем все больше и больше функций, которые должны быть последовательно объединены в цепочку, после всего лишь трех методов!

Вызов и блок решают это уникальным способом, вы можете получить почти такое же поведение, используя это:

1
2
3
4
5
6
7
updateUIToLoadingStatus();
Display.getInstance().invokeAndBlock(new Runnable() {
    public void run() {
          readAndParseFile();
    }
});
updateUIWithContentOfFile();

Вызывать и блокировать эффективно блокирует текущее EDT законным путем. Он порождает отдельный поток, который выполняет метод run (), и когда этот метод запуска завершается, он возвращается к EDT. Все события и поведение EDT все еще работают, пока invokeAndBlock работает, это потому, что invokeAndBlock () продолжает вызывать цикл основного потока внутри.

Обратите внимание, что это приводит к небольшому снижению производительности и что вложение invokeAndBlocks (или более использование их) не рекомендуется. Однако они очень удобны при работе с несколькими потоками / пользовательским интерфейсом.

Зачем мне вызывать callSerially, когда я уже на EDT?

Мы обсуждали callSerially в предыдущем посте, но одна из непонятых тем — зачем нам когда-либо вызывать этот метод, когда мы все еще на EDT. Первоначальная версия LWUIT использовалась для создания исключения IllegalArgumentException, если callSerially был вызван в EDT, поскольку это, казалось, не имело смысла.
Тем не менее, это имеет некоторый смысл, и мы можем объяснить это на примере. Например, у нас есть кнопка, функциональность которой связана с событиями, например:

  1. Пользователь добавил слушателя действия, чтобы показать диалог.
  2. Фреймворк, который установил пользователь, добавил некоторые кнопки в кнопку.
  3. Кнопка перерисовывает анимацию выпуска по мере ее выпуска.

Однако это может вызвать проблему, если первое событие, которое мы обрабатываем (диалоговое окно), может вызвать проблему для следующих событий. Например, диалог блокирует EDT (используя invokeAndBlock), события будут происходить, но, поскольку событие, в котором мы находимся, «уже произошло», кнопка перерисовывается и регистрация каркаса не происходит. Это также может произойти, если мы покажем форму, которая может вызвать логику, которая полагается на текущую форму, которая все еще присутствует.

Одно из решений этой проблемы — просто обернуть тело слушателя действия callSerially. В этом случае callSerially перенесет событие на следующий цикл (цикл) EDT и позволит завершить другие события в цепочке. Обратите внимание, что вы не должны использовать это как обычно, так как оно включает служебные данные и усложняет поток приложений, однако, когда вы сталкиваетесь с проблемами в обработке событий, я предлагаю попробовать это, чтобы увидеть, является ли это причиной.

Ссылка: CallSerially EDT & InvokeAndBlock (часть 2) от нашего партнера JCG Шая Альмога в блоге Codename One .