SurfaceView and GLSurfaceView

The Android app framework UI is based on a hierarchy of objects that start with a View. All UI elements go through a series of measurements and a layout process that fits them into a rectangular area. Then, all visible view objects are rendered to a surface that was set up by the WindowManager when the app was brought to the foreground. The app's UI thread performs layout and rendering to a buffer per frame.


A SurfaceView is a component that you can use to embed an additional composite layer within your view hierarchy. A SurfaceView takes the same layout parameters as other views, so it can be manipulated like any other view, but the SurfaceView's contents are transparent.

When you render with an external buffer source, such as GL context or a media decoder, you need to copy buffers from the buffer source to display the buffers on the screen. Using a SurfaceView enables you to do that.

When the SurfaceView's view component is about to become visible, the framework asks SurfaceControl to request a new surface from SurfaceFlinger. To receive callbacks when the surface is created or destroyed, use the SurfaceHolder interface. By default, the newly created surface is placed behind the app UI surface. You can override the default Z-ordering to put the new surface on top.

Rendering with SurfaceView is beneficial in cases where you need to render to a separate surface, such as when you render with the Camera API or an OpenGL ES context. When you render with SurfaceView, SurfaceFlinger directly composes buffers to the screen. Without a SurfaceView, you need to composite buffers to an offscreen surface, which then gets composited to the screen, so rendering with SurfaceView eliminates extra work. After rendering with SurfaceView, use the UI thread to coordinate with the activity lifecycle and make adjustments to the size or position of the view if needed. Then, the Hardware Composer blends the app UI and the other layers.

The new surface is the producer side of a BufferQueue, whose consumer is a SurfaceFlinger layer. You can update the surface with any mechanism that can feed a BufferQueue, such as surface-supplied Canvas functions, attaching an EGLSurface and drawing on the surface with GLES, or configuring a media decoder to write the surface.

SurfaceView and the activity lifecycle

When using a SurfaceView, render the surface from a thread other than the main UI thread.

For an activity with a SurfaceView, there are two separate but interdependent state machines:

  • App onCreate/onResume/onPause
  • Surface created/changed/destroyed

When the activity starts, you get callbacks in this order:

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

If you click back, you get:

  1. onPause()
  2. surfaceDestroyed() (called just before the surface goes away)

If you rotate the screen, the activity is torn down and recreated and you get the full cycle. You can tell it's a quick restart by checking isFinishing(). It's possible to start/stop an activity so quickly that surfaceCreated() happens after onPause().

If you tap the power button to blank the screen, you get only onPause() without surfaceDestroyed(). The surface remains active, and rendering can continue. You can keep getting Choreographer events if you continue to request them. If you have a lock screen that forces a different orientation, your activity may be restarted when the device is unblanked. Otherwise, you can come out of screen-blank with the same surface as before.

The lifespan of the thread can be tied to the surface or to the activity, depending on what you want to happen when the screen goes blank. The thread can start/stop either on Activity start/stop or on surface create/destroy.

Having the thread start/stop on Activity start/stop works well with the app lifecycle. You start the renderer thread in onResume() and stop it in onStop(). When creating and configuring the thread, sometimes the surface already exists, othertimes it doesn't (for example, it's still active after toggling the screen with the power button). You have to wait for the surface to be created before initializing in the thread. You can't initialize in the surfaceCreate() callback because it won't fire again if the surface wasn't recreated. Instead, query or cache the surface state, and forward it to the renderer thread.

Having the thread start/stop on surface create/destroy works well because the surface and the renderer are logically intertwined. You start the thread after the surface is created, which avoids some interthread communication concerns; and surface created/changed messages are simply forwarded. To ensure that rendering stops when the screen goes blank and resumes when it un-blanks, tell Choreographer to stop invoking the frame draw callback. onResume() resumes the callbacks if the renderer thread is running. However, if you animate based on elapsed time between frames, there could be a large gap before the next event arrives; using an explicit pause/resume message can solve this issue.

Both options, whether the lifespan of the thread is tied to the Activity or the surface, focus on how the renderer thread is configured and whether it's executing. A related concern is extracting state from the thread when the activity is killed (in onStop() or onSaveInstanceState()); in such cases, tying the lifespan of the thread to the activity works best because after the renderer thread has been joined, the rendered thread's state can be accessed without synchronization primitives.


The GLSurfaceView class provides helper classes for managing EGL contexts, interthread communication, and interaction with the activity lifecycle. You don't need to use a GLSurfaceView to use GLES.

For example, GLSurfaceView creates a thread for rendering and configures an EGL context there. The state is cleaned up automatically when the activity pauses. Most apps don't need to know anything about EGL to use GLES with GLSurfaceView.

In most cases, GLSurfaceView can make working with GLES easier. In some situations, it can get in the way.