← Назад к вопросам

Перерисуется ли View если дернуть 10 раз invalidate

2.0 Middle🔥 181 комментариев
#UI и вёрстка#Производительность и оптимизация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизм работы invalidate() и перерисовка View

Короткий ответ: нет, View не перерисуется 10 раз при последовательном вызове invalidate() 10 раз подряд в одном потоке выполнения (например, в основном UI-потоке). Система сгруппирует эти запросы в единую операцию перерисовки благодаря механизму оптимизации и синхронизации обновлений UI.

Как работает invalidate()

Метод invalidate() не вызывает немедленную перерисовку View. Вместо этого он выполняет следующее:

// Упрощенная логика метода invalidate()
public void invalidate() {
    // 1. Помечает область View как "грязную"
    // 2. Добавляет область в очередь обновлений
    // 3. Запланирует перерисовку на следующем кадре
}

Ключевые этапы обработки:

  1. Пометка области как "грязной" - View помечается как требующая перерисовки
  2. Добавление в очередь обновлений - Запрос помещается в специальную очередь ViewRootImpl
  3. Синхронизация с системой отрисовки - Система ждет следующего VSYNC-сигнала
  4. Выполнение перерисовки - Только при следующем цикле отрисовки

Механизм оптимизации

Консолидация запросов

Система Android использует объединение (coalescing) запросов на обновление. Когда вы вызываете invalidate() несколько раз до следующего кадра:

Время:   0ms    5ms    10ms   16ms (новый кадр)
События: inv1 → inv2 → inv3 → Перерисовка

Все вызовы invalidate() между кадрами будут объединены в одну операцию перерисовки.

Пример кода, демонстрирующий это поведение:

public class ExampleView extends View {
    private int drawCount = 0;
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCount++;
        Log.d("ExampleView", "Перерисовка #" + drawCount);
    }
    
    public void triggerMultipleInvalidates() {
        // 10 последовательных вызовов
        for (int i = 0; i < 10; i++) {
            invalidate();
            Log.d("ExampleView", "Вызов invalidate #" + (i + 1));
        }
    }
}

Результат выполнения:

Вызов invalidate #1
Вызов invalidate #2
...
Вызов invalidate #10
Перерисовка #X  // Только одна перерисовка!

Архитектурные особенности

Роль Choreographer и ViewRootImpl

  • Choreographer - координирует время отрисовки с VSYNC
  • ViewRootImpl - управляет очередью обновлений через TraversalRunnable
  • Очередь сообщений (MessageQueue) - обрабатывает запросы на перерисовку

Схема обработки:

// Псевдокод обработки в системе
class ViewRootImpl {
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // Отправляем задачу на следующий кадр
            mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL,
                mTraversalRunnable, null);
        }
    }
}

Исключения и особые случаи

1. Вызовы из разных потоков

Если вызывать invalidate() из разных потоков, может произойти несколько перерисовок из-за гонки условий (race conditions):

// Неправильно - может вызвать множественные перерисовки
new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        view.invalidate();
    }
}).start();

new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        view.invalidate();
    }
}).start();

2. Использование postInvalidate()

Для фоновых потоков используйте postInvalidate(), который безопасно передает запрос в UI-поток:

// Правильно для фоновых потоков
new Thread(() -> {
    for (int i = 0; i < 10; i++) {
        view.postInvalidate();
    }
}).start();

3. Принудительная немедленная перерисовка

Если действительно нужна немедленная перерисовка (что редко требуется), можно использовать:

// НЕ РЕКОМЕНДУЕТСЯ для обычного использования
view.invalidate();
view.post(() -> {
    // Принудительное обновление
    view.getViewTreeObserver().dispatchOnGlobalLayout();
});

Практические рекомендации

Когда использовать invalidate():

  • При изменении данных, влияющих на отрисовку
  • При анимациях (совмещать с ValueAnimator или ObjectAnimator)
  • При изменении размеров или положения

Что избегать:

  • Избыточных вызовов в циклах - используйте флаги
  • Вызовов из фоновых потоков без postInvalidate()
  • Ручных микро-оптимизаций - доверяйте системе Android

Производительность

Система Android рассчитана на 60 FPS (каждые ~16.6ms). Даже если вызвать invalidate() 100 раз за один кадр, система:

  1. Объединит все запросы
  2. Выполнит одну перерисовку на следующем VSYNC
  3. Сохранит плавность интерфейса

Вывод: Android оптимизирует перерисовки, чтобы минимизировать нагрузку на систему и батарею, обеспечивая при этом отзывчивый пользовательский интерфейс.

Перерисуется ли View если дернуть 10 раз invalidate | PrepBro