Android: Отображение оценки в виде строки на canvas создает новую строку для каждой команды рисования. Как мне обойти это?

#android #string

#Android #строка

Вопрос:

Я создаю игру, которая отображает некоторые цифры на холсте (счет, время и т.д.).

В настоящее время я делаю это с помощью команды drawtext на canvas

 // score is some int
draw(Canvas c) {
    c.drawText(score "", x, y, paintSyle);
}
  

Я слышал, что создание объекта и сборка мусора — дорогостоящие операции, и я думаю, что это создает новую строку при каждом ее вызове.

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

Было бы быстрее / лучше создать (или найти?) какой-нибудь изменяемый подкласс string и обойти эту проблему? Есть ли другой способ решить эту проблему? Или это просто так?

Ответ №1:

Введите две новые private переменные-члены String renderedScoreString и int rederedScore и перепишите свой draw() метод следующим образом:

 draw(Canvas c) {
    if (this.score != this.renderedScore || this.renderedScoreString == null) {
        this.renderedScore = this.score;
        this.renderedScoreString = Integer.toString(this.renderedScore);
    }
    c.drawText(this.renderedScore, x, y, paintStyle);
}
  

это должно вас сильно сэкономить! создания объектов. Вы также могли бы скрыть шаблонный код за методом получения, например, String getScoreString() который делает то же самое, поэтому у вас его нет в draw() -методе.

Комментарии:

1. Спасибо, я вижу, как это будет работать для целого числа, такого как оценка, которое меняется не часто, но как насчет чего-то вроде time, которое меняется каждую секунду / миллисекунду? Мне действительно интересно, есть ли способ полностью избежать создания объекта. Также я думаю, что если я изменю renderedScoreString, где я изменил оценку, мне даже не пришлось бы выполнять проверку здесь.

2. если что-то меняется очень часто, всегда отображается и постоянно генерирует новые значения (не кэшируется), вам нужно создавать строковые объекты по мере необходимости. Иногда вы могли бы использовать следующее решение: кэшировать строковые объекты от 0 до 99 в hashmap и отображать время не за один draw() вызов. Пример: draw(stringMap[hours], ...); draw(stringMap[minutes], ...); draw(stringMap[seconds],...) . stringMap может быть простым массивом…

Ответ №2:

Мой друг подсказал мне решение этой проблемы. Когда вы хотите нарисовать что-то с течением времени, один из лучших (и простейших) механизмов для этого — разделить то, что вам нужно сделать, на два совершенно отдельных процесса.

ie. Используйте команду draw исключительно для рисования материалов, сведите логику / назначение в Draw () к абсолютному минимуму.

 private final long TIMER_PERIOD = 500;
private String timeString;
private Runnable updateRunnable;
private Handler updateHandler = new Handler();

public void onCreate(Bundle savedInstanceState) {
    updateRunnable = new Runnable() {
        @Override
        public void run() {
            timeString = GetTimeString();
            updateHandler.postDelayed(updateRunnable, TIMER_PERIOD);
        }
    }
}

Draw(Canvas c) {
    c.drawText(timeString, x, y, paintStyle);
}
  

В этом примере команда рисования просто принимает timeString в его текущем состоянии и выводит его на экран. Это высокоэффективное использование функции рисования, поскольку она не требует создания какого-либо объекта, и в ней нет логики, которая не требуется немедленно для выполнения любого рисования. . В фоновом режиме Runnable выполняет функцию run() каждые 500 миллисекунд (приблизительно). Просто обновите функцию Run () с любой логикой, которая вам нужна для вычисления времени (в примере используется фиктивная функция GetTimeString() )

Я надеюсь, что это полезно.

Комментарии:

1. GetTimeString по-прежнему будет создавать объекты каждые 1/2 секунды. Я думаю, это намного лучше, чем так быстро, как может рисовать программа. Я понимаю, что вы убрали ее из команды рисования, и это нормально, но когда я задавал вопрос (1,5 года назад : D), я пытался создать постоянное количество объектов.

Ответ №3:

Я знаю, что я восстанавливаю мертвый поток, но есть одна дополнительная оптимизация, которую вы можете добавить к этому, которая ограничивает создание строки одноразовым действием и, таким образом, запускает GC только один раз в начале, а не во время игры (что довольно важно для игры на Android).

Где-нибудь во время запуска вашей игры (onCreate, onResume, как часть синглтона во время запуска приложения и т.д.) Создайте большую строку [], Которая может содержать максимальное количество баллов (моя игра заполняет массив 10000, поэтому максимальное количество баллов будет равно 9999). Затем выполните цикл над ним с помощью цикла for, заполнив каждый индекс строкой.valueOf(i).

 for (int i = 0; i <scoreStrings.length; i  )
        {
            scoreStrings[i] = String.valueOf(i);
        }
  

Теперь, когда вам нужно нарисовать партитуру, просто используйте int, который вы используете для сохранения партитуры в качестве индекса к этому массиву, и «эй, вуаля!», вы получите правильную строку для вашей партитуры.

 canvas.drawText(scoreStrings[score], x, y, paint);