Статьи

Лучшие практики для мобильных приложений JavaFX, часть 1

Как теперь знают все, кто интересуется JavaFX, JavaFX Mobile был выпущен в скором времени.
тому назад. Это была адская поездка, это точно. Я чувствовал себя настолько измотанным, что у меня даже не было энергии для блога во время выпуска

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

ВНИМАНИЕ : Советы, которые я даю здесь, верны для текущей версии JavaFX Mobile, которая является частью JavaFX 1.1 SDK. В будущих версиях поведение будет меняться,
текущая плохая производительность упомянутых артефактов будет оптимизирована или, по крайней мере, значительно улучшена. Все, о чем я пишу здесь — это снимок, ничего не следует понимать как
финал!

Пункт 1: Избегайте ненужных привязок
Привязки очень удобны, без сомнения, одно из самых ценных нововведений в JavaFX Script. К сожалению, они приходят с ценой. Сгенерированный код котельной плиты обычно не такой маленький и
быстрая ручная реализация. Особенно сложные структуры зависимостей имеют тенденцию накладывать серьезные ограничения на производительность и занимаемую площадь.

По этой причине рекомендуется максимально избегать привязок. Часто ту же функциональность можно реализовать с помощью триггеров. Не следует использовать привязки, чтобы избежать проблем с порядком инициализации. И это, конечно, не имеет смысла связывать с постоянным значением.
Ленивые привязки в большинстве случаев (но не всегда!) Быстрее, если связанная переменная обновляется чаще, чем читается, но они все еще не так быстры, как ручные реализации.

пример
Обычный вариант использования — это количество узлов, положение и размеры которых зависят от размера сцены. Типичная реализация использует привязки для достижения этой цели.
Здесь мы рассмотрим простой пример, который напоминает такую ​​ситуацию. Сцена состоит из трех прямоугольников, которые расположены по диагонали от левого верхнего до правого нижнего угла. Размер прямоугольника составляет четверть размера экрана. Пример кода 1 показывает реализацию с привязками.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
def rectangleWidth: Number = bind stage.width * 0.25;
 def rectangleHeight: Number = bind stage.height * 0.25;
 
 def stage: Stage = Stage {
     scene: Scene {
         content: for (i in [0..2])
             Rectangle {
                 x: bind stage.width * (0.125 + 0.25*i)
                 y: bind stage.height * (0.125 + 0.25*i)
                 width: bind rectangleWidth
                 height: bind rectangleHeight
             }
     }
 }

Пример кода 1: макет рассчитывается с привязками
Первый вопрос, о котором следует подумать, действительно ли привязки необходимы. На реальном устройстве размер экрана изменяется только при переключении ориентации экрана (при условии, что устройство поддерживает эту функцию). Если наше приложение не поддерживает поворот экрана, макет можно определить постоянным.
Одно из возможных решений для уменьшения количества привязок показано в примере кода 2. Введены две переменные width и height, которые связаны с stage.width и stage.height соответственно. Их единственная цель — предоставить триггеры для stage.width и stage.height, поскольку мы не хотим переопределять исходные триггеры. Положение и размер прямоугольников рассчитываются вручную в триггерах.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
def r = for (i in [0..2]) Rectangle {}
 
 def stage = Stage {
     scene: Scene {content: r}
 }
 
 def height = bind stage.height on replace {
     def rectangleHeight = height * 0.25;
     for (i in [0..2]) {
         r[i].height = rectangleHeight;
         r[i].y = height * (0.125 + 0.25*i)
     }
 }
 
 def width = bind stage.width on replace {
     def rectangleWidth = width * 0.25;
     for (i in [0..2]) {
         r[i].width = rectangleWidth;
         r[i].x = width * (0.125 + 0.25*i)
     }
 }

Пример кода 2: макет, рассчитанный в триггере
Без сомнения, код в примере кода 1 более элегантный. Но, измеряя производительность обоих фрагментов в эмуляторе, оказалось, что код в примере кода 2 почти в два раза быстрее.

Далее мы увидим второй совет, который позволит повысить производительность мобильных приложений JavaFX. Я думаю, что этот и предыдущий являются наиболее важными.

ВНИМАНИЕ : Советы, которые я даю здесь, верны для текущей версии JavaFX Mobile, которая является частью JavaFX 1.1 SDK. В будущих версиях поведение будет меняться,
текущая плохая производительность упомянутых артефактов будет оптимизирована или, по крайней мере, значительно улучшена. Все, о чем я пишу здесь — это снимок, ничего не следует понимать как
финал!

Пункт 2: сделайте сценограф как можно меньше
За кулисами среды исполнения происходит много общения для обновления переменных узлов в графе сцены. Чем больше элементов у сценографа, тем больше связи требуется. Поэтому очень важно, чтобы сценограф был как можно меньше. Особенно анимации имеют тенденцию страдать от большого сценографа.
Это плохая практика — постоянно держать узел в графе сцены и контролировать его видимость с помощью флага visible или его непрозрачности. Невидимые узлы в графе сцены по-прежнему являются частью коммуникационного цирка на заднем плане. Вместо этого следует удалить узлы из графа сцены и добавлять их только при необходимости.
Этот подход имеет один недостаток, хотя. Добавление или удаление узлов занимает больше времени, чем настройка видимости. Поэтому это может быть неуместно в ситуациях, когда немедленные ответы имеют решающее значение.

Пример 1
Часто у одного есть набор узлов, из которых виден только один. Это могут быть, например, разные страницы или узлы для визуализации разных состояний элемента. Может возникнуть соблазн добавить все узлы к графу сцены и установить только текущий как видимый.

Пример кода 1 показывает упрощенную версию этого подхода. Три цветных круга созданы для визуализации какого-либо состояния (красный, желтый, зеленый). Только один узел виден в любое время. (Давайте на секунду проигнорируем, что это может быть просто достигнуто путем изменения цвета заливки одного круга. В реальных приложениях, вероятно, будут визуализации или более сложные формы для визуализаций, и простое изменение цвета не будет работать.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 
 var state: Integer;
 
 Stage {
     scene: Scene {
         content: for (i in [0..2])
             Circle {
                 centerX: 10
                 centerY: 10
                 radius: 10
                 fill: colors[i]
                 visible: bind state == i
             }
     }
 }

Пример кода 1: Использование видимости для переключения между узлами
Это приводит к трем узлам в графе сцены, хотя показан только один. Это должно быть изменено, чтобы гарантировать, что только видимый узел находится в графе сцены. Пример кода 2 показывает одну возможную реализацию.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 
 var state: Integer on replace oldValue {
     insert nodes[state] into stage.scene.content;
     delete nodes[oldValue] from stage.scene.content;
 }
 
 
 def nodes = for (i in [0..2])
     Circle {
         centerX: 10
         centerY: 10
         radius: 10
         fill: colors[i]
     }
 
 def stage = Stage {scene: Scene{}}

Пример кода 2: добавление и удаление узлов при необходимости
Код в примере кода 1 более компактен, но в примере кода 2 количество узлов в графе сцены уменьшено с трех до одного. При настройке некоторых демонстраций для выпуска JavaFX Mobile мы смогли сократить количество узлов в графе сцены на 50% и более, просто за счет того, что в него входят только видимые узлы.

Пример 2
Если узлы показываются и скрываются с помощью какой-либо анимации, добавление и удаление узла в графе сцены становится чрезвычайно простым. Нужно только реализовать действие в начале fadeIn-анимации и в конце fadeOut-анимации, чтобы добавить соответственно удалить узел. Пример кода 3 демонстрирует такое использование, когда простое окно сообщения отображается и скрывается путем изменения непрозрачности.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def msgBox = Group {
     opacity: 0.0
     content: [
         Rectangle {width: 150, height: 40, fill: Color.GREY},
         Text {x: 20, y: 20, content: "Hello World!"}
     ]
 }
 
 def fadeIn = Timeline {
     keyFrames: [
         KeyFrame {
             action: function() {insert msgBox into stage.scene.content}
         },
         at (1s) {msgBox.opacity => 1.0 tween Interpolator.LINEAR}
     ]
 }
 
 def fadeOut = Timeline {
     keyFrames: KeyFrame {
         time: 1s
         values: msgBox.opacity => 0.0 tween Interpolator.LINEAR
         action: function() {delete msgBox from stage.scene.content}
     }
 }
 
 def stage = Stage {scene: Scene{}}

Пример кода 3: Использование fadeIn- и fadeOut-анимаций для добавления и удаления узлов.

Ссылка: Рекомендации для мобильных приложений JavaFX и Рекомендации для мобильных приложений JavaFX 2 от нашего партнера JCG