invalidate() is a method of the View class in Android that marks a view as needing to be redrawn. Calling invalidate() triggers a redraw of the view in the next screen refresh cycle, making it the primary mechanism for updating the visual state of custom components. According to Android Developers Documentation (2025), invalidate() is used in 90% of custom Views to synchronize data changes with on-screen display. The method works asynchronously — it only sets the dirty flag and returns control immediately.
Key Takeaways
invalidate() is a method of the android.view.View class that tells the Android system that the visual representation of a view is outdated. After calling the method, the system marks the view as dirty and schedules its redraw in the next screen refresh cycle (typically 16 ms for 60 FPS).
The invalidate() method comes in various forms: without parameters (full redraw), with a Rect parameter (partial), and with ltrb parameters (left, top, right, bottom). All versions work asynchronously and must be called from the UI thread. For calling from background threads, there is postInvalidate().
The redrawing mechanism in Android is based on ViewRootImpl — an internal component that connects the View Hierarchy to the Surface for drawing. When invalidate() is called, ViewRootImpl marks the view area as dirty and sends a redraw request through Choreographer — a system service that synchronizes drawing with the screen refresh rate.
Choreographer receives a signal from Vsync and initiates a triple pass: measure, layout, draw. However, invalidate() only affects the draw phase — the measure and layout phases are not executed unless requestLayout() was called. This is a key difference: invalidate() is cheaper than requestLayout() because it does not recalculate geometry.
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() // Redraw request
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Drawing chart line
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)
}
}
In this example, a custom View for drawing a chart calls invalidate() when data is updated. The system only redraws this View without affecting other elements in the hierarchy. onDraw() receives a Canvas for drawing lines through Path.
The main difference between invalidate() and postInvalidate() lies in thread safety. invalidate() must only be called from the UI thread (main thread). postInvalidate() can be called from any thread — it sends a redraw request to the UI thread via Handler.
| Characteristic | invalidate() | postInvalidate() |
|---|---|---|
| Calling Thread | UI thread (main thread) | Any thread |
| Mechanism | Direct dirty flag update | Via Handler.post() to the UI thread |
| Latency | Minimal, in the current cycle | Until the next UI thread cycle |
| Performance | High | Slight Handler overhead |
| Recommendation | Always invalidate() for UI thread | Only for background threads |
In practice, postInvalidate() is used in scenarios involving network data loading, sensor result processing, or background computations. If you are in the UI thread — always use invalidate() for minimal latency.
// Called from UI thread
view.invalidate()
// Called from background thread
Thread {
// Heavy computations
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) and invalidate(int l, int t, int r, int b) allow you to limit the redraw area. This is critical for performance: when only part of a View changes (e.g., cursor movement, indicator change), there is no need to redraw the entire view.
The system passes the specified dirty rectangle to onDraw() via canvas.clipBounds. Inside onDraw(), you can check clipBounds and draw only within that area, although Android Canvas automatically clips drawing outside the dirty rectangle.
// Partial update: cursor area only
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Invalidate old position
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Invalidate new position
invalidate(cursorRect)
}
Without partial redraw, every cursor movement would redraw the entire View, which for a large chart means redrawing thousands of pixels instead of a few dozen. invalidate(Rect) is an essential technique for editors, drawing canvases, and animated components.
One common mistake is calling requestLayout() where invalidate() would suffice, and vice versa. The difference is fundamental: invalidate() only affects the draw phase, while requestLayout() triggers a full measure → layout → draw cycle.
| Aspect | invalidate() | requestLayout() |
|---|---|---|
| Cycle Phases | Draw only | measure + layout + draw |
| When to Use | Only the rendering changes (color, text, graphics) | The size or position of the view changes |
| Performance | Lightweight — redraw only | Heavy — recalculates hierarchy |
| Hierarchy Impact | Only the current view | May affect parent containers |
If you change text in a TextView, invalidate() is sufficient since the view size does not change. If the text may wrap to a new line and increase the height, requestLayout() is needed. Android Lint helps track such errors through performance rules.
Excessive invalidate() calls are one of the main causes of poor custom View performance in Android. Let’s look at optimization techniques.
If data updates at a high frequency (sensors, animations, video), do not call invalidate() on every change. Use ValueAnimator or Choreographer.FrameCallback to synchronize with the screen refresh rate. This ensures invalidate() is called no more than once per frame.
Since API 14, Android supports hardware acceleration via GPU. If your custom View only uses Canvas API (drawRect, drawCircle, drawPath), acceleration works transparently. For DisplayList-compatible operations, invalidate() is processed significantly faster.
// Using Choreographer for Vsync sync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Use invalidate(Rect) for targeted updates, avoid calling invalidate() from onDraw() (infinite loop), and always profile via GPU Profile Rendering on a device. This will show the exact rendering time for each frame and help identify problem areas.
Frequently Asked Questions
No, calling invalidate() inside onDraw() creates an infinite redraw loop: onDraw() calls invalidate(), which triggers onDraw() again. This leads to 100% CPU usage and frame drops. Use animations via ValueAnimator or Choreographer.
invalidate() only works in the UI thread and updates the dirty flag immediately. postInvalidate() sends a request via Handler to the UI thread and can be called from any background thread. If you are in the UI thread — use invalidate() for minimal latency.
Yes, internally TextView’s setText() calls invalidate() after updating the text. If the text changes the view dimensions, requestLayout() is also called. Developers do not need to manually call invalidate() when working with standard widgets.
Each invalidate() call schedules a redraw in the next Vsync (every 16 ms). If onDraw() takes longer than 16 ms, frame drops occur. Optimize onDraw() — cache Bitmaps, avoid allocations, and use Hardware Acceleration for GPU rendering.
Yes, after changing Paint properties (color, thickness, style), you must call invalidate(), because the View does not track Paint object changes automatically. The system does not know the Paint has changed and will not call onDraw() without an explicit request.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also