invalidate() — какво е, механизъм на прерисуване и invalidate(Rect)

Автор: IT Sectr Публикувано: 2026-07-20 Време за четене: 7 мин

invalidate() — е метод на класа View в Android, който маркира изгледа като изискващ повторно рисуване. Извикването на invalidate() води до прерисуване на изгледа в най-близкия цикъл на обновяване на екрана, което го прави основния механизъм за обновяване на визуалното състояние на потребителски компоненти. Според Android Developers Documentation (2025), invalidate() се използва в 90% от потребителските View за синхронизиране на промени в данните с показването на екрана. Методът работи асинхронно — той само задава флага dirty и веднага връща управлението.

Основни точки

  • invalidate() — асинхронна заявка за прерисуване на View в Android, работи чрез механизъм dirty flag
  • postInvalidate() — версия на invalidate() за извикване от фонова нишка, безопасна за нишки
  • invalidate(Rect) — частично прерисуване само на указаната област за оптимизиране на производителността
  • onDraw() — метод, който се извиква от системата след invalidate(), аналогичен на draw(_:) в iOS
  • invalidate() vs requestLayout() — invalidate() прерисува съдържанието, requestLayout() преизчислява геометрията

Какво е invalidate() в Android

invalidate() — е метод на класа android.view.View, който уведомява системата Android, че визуалното представяне на изгледа е остаряло. След извикването на метода, системата маркира изгледа като dirty и планира неговото прерисуване в най-близкия цикъл на обновяване на екрана (обикновено 16 ms за 60 FPS).

Методът invalidate() приема различни форми: без параметри (пълно прерисуване), с параметър Rect (частично) и с параметри ltrb (left, top, right, bottom). Всички версии работят асинхронно и могат да бъдат извикани от UI нишката. За извикване от фонови нишки съществува postInvalidate().

Как работи прерисуването чрез invalidate()

Механизмът на прерисуване в Android се основава на ViewRootImpl — вътрешен компонент, който свързва View Hierarchy с Surface за рисуване. Когато се извика invalidate(), ViewRootImpl маркира областта на изгледа като dirty и изпраща заявка за прерисуване чрез Choreographer — системна услуга, синхронизираща рисуването с честотата на обновяване на екрана.

Цикъл на прерисуване

Choreographer получава сигнал от Vsync и стартира троен проход: measure, layout, draw. Въпреки това, invalidate() засяга само фазата draw — фазите measure и layout не се изпълняват, освен ако не е извикан requestLayout(). Това е ключовата разлика: invalidate() е по-евтин от requestLayout(), защото не преизчислява геометрията.

kotlin
class CustomChartView(context: Context, attrs: AttributeSet?)
    : View(context, attrs) {

    private var dataPoints: List<Float> = emptyList()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    fun updateData(newPoints: List<Float>) {
        dataPoints = newPoints
        invalidate() // Заявка за прерисуване
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.color = Color.BLUE
        paint.strokeWidth = 4f
        paint.style = Paint.Style.STROKE

        // Рисуване на линия на графика
        val path = Path()
        dataPoints.forEachIndexed { index, value ->
            val x = index * width / max(dataPoints.size - 1, 1)
            val y = height - value * height
            if (index == 0) path.moveTo(x, y)
            else path.lineTo(x, y)
        }
        canvas.drawPath(path, paint)
    }
}

В този пример, потребителски View за рисуване на графика извиква invalidate() при обновяване на данни. Системата прерисува само този View, без да засяга останалите елементи на йерархията. onDraw() получава Canvas за рисуване на линии чрез Path.

invalidate() vs postInvalidate()

Основната разлика между invalidate() и postInvalidate() е в безопасността на нишките. invalidate() трябва да се извиква само от UI нишката (главната нишка). postInvalidate() може да се извика от всяка нишка — чрез Handler изпраща заявка за прерисуване до UI нишката.

Характеристикаinvalidate()postInvalidate()
Нишка на извикванеUI нишка (главна нишка)Всяка нишка
МеханизъмДиректно обновяване на dirty flagЧрез Handler.post() до UI нишката
ЛатентностМинимална, в текущия цикълДо следващия цикъл на UI нишката
ПроизводителностВисокаМалък overhead върху Handler
ПрепоръкаЗа UI нишка винаги invalidate()Само за фонови нишки

На практика, postInvalidate() се използва в сценарии за зареждане на данни от мрежа, обработка на резултати от сензори или фонови изчисления. Ако сте в UI нишката — винаги използвайте invalidate() за минимална латентност.

kotlin
    // Извикано от UI нишка
view.invalidate()

    // Извикано от фонова нишка
Thread {
    // Тежки изчисления
    val result = performHeavyCalculation()
    runOnUiThread {
        updateUi(result)
    }
}.start()

Частично прерисуване чрез invalidate(Rect)

invalidate(Rect) и invalidate(int l, int t, int r, int b) позволяват ограничаване на областта за прерисуване. Това е критично за производителността: при обновяване само на част от View (например движение на курсора, промяна на индикатор) няма смисъл да се прерисува целия изглед.

Системата предава указания dirty правоъгълник на onDraw() чрез canvas.clipBounds. Вътре в onDraw() може да се провери clipBounds и да се рисува само в границите на тази област, въпреки че Android Canvas автоматично изрязва рисуването извън dirty правоъгълника.

kotlin
    // Частично обновяване: само област на курсора
private val cursorRect = Rect()

fun moveCursorTo(newX: Int, newY: Int) {
    // Анулиране на стара позиция
    invalidate(cursorRect)

    cursorRect.set(newX - 5, newY - 5,
                  newX + 5, newY + 5)

    // Анулиране на нова позиция
    invalidate(cursorRect)
}

Без частично прерисуване, всяко движение на курсора би прерисувало целия View, което за голяма графика означава прерисуване на хиляди пиксели вместо няколко десетки. invalidate(Rect) — задължителна техника за редактори, платна за рисуване и анимирани компоненти.

invalidate() vs requestLayout(): каква е разликата

Една от честите грешки е извикването на requestLayout() там, където е достатъчен invalidate(), и обратно. Разликата е фундаментална: invalidate() засяга само фазата draw, докато requestLayout() стартира пълния цикъл measure → layout → draw.

Аспектinvalidate()requestLayout()
Фази на цикълаСамо drawmeasure + layout + draw
Кога да се използваПроменя се само рисуването (цвят, текст, графика)Променя се размерът или позицията на изгледа
ПроизводителностЛеко — само прерисуванеТежко — преизчисляване на йерархията
Влияние върху йерархиятаСамо текущия изгледМоже да засегне родителски контейнери

Ако променяте текст в TextView — достатъчен е invalidate(), тъй като размерът на изгледа не се променя. Ако текстът може да премине на нов ред и да увеличи височината — е необходим requestLayout(). Android Lint помага за проследяване на такива грешки чрез правила за производителност.

Оптимизиране на производителността на invalidate()

Прекомерните извиквания на invalidate() — една от основните причини за ниска производителност на потребителски View в Android. Нека разгледаме техники за оптимизация.

Минимизирайте честотата на извиквания

Ако данните се обновяват с висока честота (сензори, анимация, видео), не извиквайте invalidate() при всяка промяна. Използвайте ValueAnimator или Choreographer.FrameCallback за синхронизация с честотата на обновяване на екрана. Това гарантира, че invalidate() се извиква не повече от веднъж на кадър.

Използвайте Hardware Acceleration

От API 14 Android поддържа хардуерно ускорение чрез GPU. Ако вашият потребителски View използва само Canvas API (drawRect, drawCircle, drawPath), ускорението работи прозрачно. За операции, съвместими с DisplayList, invalidate() се обработва значително по-бързо.

kotlin
// Използване на Choreographer за Vsync синхронизация
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
    updateAnimation(frameTimeNanos)
    invalidate()
    Choreographer.getInstance().postFrameCallback(this)
}

fun startAnimation() {
    Choreographer.getInstance().postFrameCallback(frameCallback)
}

Използвайте invalidate(Rect) за точкови обновявания, избягвайте извикването на invalidate() от onDraw() (безкраен цикъл) и винаги профилирайте чрез GPU Profile Rendering на устройството. Това ще покаже точното време за рисуване на всеки кадър и ще помогне за идентифициране на проблемни места.

Често задавани въпроси

Може ли да се извиква invalidate() от onDraw()?

Не, извикването на invalidate() вътре в onDraw() създава безкраен цикъл на прерисуване: onDraw() извиква invalidate(), който отново стартира onDraw(). Това води до 100% натоварване на CPU и загуба на кадри. Използвайте анимации чрез ValueAnimator или Choreographer.

Как се различава invalidate() от postInvalidate()?

invalidate() работи само в UI нишката и обновява dirty flag незабавно. postInvalidate() изпраща заявка чрез Handler до UI нишката и може да бъде извикан от всяка фонова нишка. Ако сте в UI нишката — използвайте invalidate() за минимална латентност.

Дали setText() в TextView автоматично извиква invalidate()?

Да, вътре в TextView методът setText() извиква invalidate() след обновяване на текста. Ако текстът е променил размерите на изгледа, допълнително се извиква requestLayout(). Разработчикът не трябва ръчно да извиква invalidate() при работа със стандартни Widget-и.

Как invalidate() влияе на производителността при 60 FPS?

Всяко извикване на invalidate() планира прерисуване в следващия Vsync (на всеки 16 ms). Ако onDraw() отнема повече от 16 ms, настъпва загуба на кадри. Оптимизирайте onDraw() — кеширайте Bitmap, избягвайте алокации и използвайте Hardware Acceleration за GPU рисуване.

Трябва ли да се извиква invalidate() след промяна на свойствата на Paint?

Да, след промяна на свойствата на Paint (цвят, дебелина, стил) трябва да се извика invalidate(), тъй като View не проследява автоматично промените в обектите Paint. Системата не знае, че Paint се е променил, и няма да извика onDraw() без изрична заявка.

Резюме

  • invalidate() — основният механизъм за заявка за прерисуване на View в Android, работи асинхронно чрез dirty flag
  • postInvalidate() — безопасна за нишки версия за извикване от фонови нишки, използва Handler
  • invalidate(Rect) — частично прерисуване само на указаната област, критично за производителност при точкови промени
  • requestLayout() — стартира пълния цикъл measure + layout + draw, значително по-тежък от invalidate()
  • Choreographer — системна услуга за синхронизация с Vsync, препоръчва се за анимации с invalidate()
  • Hardware Acceleration — GPU ускорение достъпно от API 14, ускорява обработката на invalidate() за Canvas API
  • GPU Profile Rendering — инструмент за профилиране за измерване на времето за рисуване и идентифициране на бавни onDraw()

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също