How did Android SurfaceView Works?

Jacob su
6 min readApr 26, 2020

--

SurfaceView was not a normal view like others in android, according to the official document, it’s a view that can draw to a Surface and output to the SurfaceFlinger directly. So before understanding SurfaceView, we need to familiar Surface first, again according to the document, Surface is an interface for a producer to exchange buffers with a consumer, and there is a BufferQueue that connects the producer and consumer, but let’s ignore the BufferQueue, and don’t make thing complicated.

Come back to this case, the producer is the SurfaceView, and the consumer is the SurfaceFlinger, which would negotiate with the HWComposer to decide which Surfaces would be merged by itself and send the merged result Surface to HWComposer. Again, don’t make things complicated, let’s ignore HWComposer and take surface as image Layer here.

So, the document said:

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

The above paragraph described How SurfaceView works, but I guess there are only a few developers who can figure out what it said. So that’s the reason why I wrote this article.

First, we need some context to help us digest the above obscure terminology. We already know our simplified SurfaceFlinger explanation, it accepts the Surfaces, or by another term “Image Layers”, then combine them to the display. So which objects hold the Surface and produce the images to the SurfaceFlinger: SurfaceView, and Window.

The Activity has a window that the views can be attached to, the dialogs are windows, so the window is the place where the views attached and render. Then the window would output the views’ combined image to the SurfaceFlinger. while the SurfaceView is not a normal view, it wouldn’t render to its attached window’s Surface, instead, it would output its image to the SurfaceFlinger through its own Surface directly.

The surface is Z ordered

the SurfaceView punches a hole in its window

Let’s explain those two lines by using other words. The Surface output to the image layer of the SurfaceFlinger and the image layer was Z ordered, so the SurfaceFlinger would know which layer was on the top and overlay another layer. The SurfaceView’s corresponding layer’s Z-order was always below its attached Window’s layer. So the reasonable result would be SurfaceView will always invisible, which was not the truth of course.

I would mention here, the Surface’s Z-order was not configured by the App developer but the Android System. So it’s quite different from the SurfaceView’s Z-order, which can be configured by its index sequence in its parent view and its elevation value.

The explanation is that the SurfaceView would always perform like a transparent Rect to its Window layer, that’s document said: the SurfaceView punches a hole in its window. So the SurfaceFlinger would combine the SurfaceView’s image layer with its window’s image layer overlay it.
Because the SurfaceView’s position in its window was transparent, so when combined the two layers the SurfaceView’s layer would be visible, only if the window’s child views wouldn’t fill the SurfaceView’s Rect with opaque pixels.

It almost gets closer right. Let’s put some muds in, according to the above explanation, the SurfaceView’s Surface was always below the window’s one, so event the view below the SurfaceView would appear to overlay the SurfaceView. But that’s not the truth. The View below the SurfaceView would always be invisible, the reasonable explanation is that the window system would do this job by hiding the normal view below the SurfaceView. That’s a good answer, but what if the SurfaceView rendering a texture with alpha channel to its Surface, then according to this explanation, the normal view below the SurfaceView would still be invisible in this scenario. Did you get it? The Window Surface and SurfaceView Surface belong to different Layers, we can put one layer below or above another layer, but it’s impossible to insert the SurfaceView’s layer inside the Window Surface’s layer. A layer is a thin 2D plane. There is an OpenGL term named “Blending” for merge pixels of textures in the depth, that’s the theory that merges two textures, it’s not helpful to understood this, I just mention it here, if it confuses you, ignore it.

So, it’s time to do some exploration and get your hand dirty in this scenario, I wrote a demo, try to render the SurfaceView with alpha channel. Then I found it’s impossible to render alpha channel to a SurfaceView. That means the Normal View below the SurfaceView would always be invisible, then it makes everything reasonable. The Views below the SurfaceView was always cut down by the window, it would never be rendered to the window’s Surface, only the view above the SurfaceView would be rendered to the window’s surface.

Basically, until now, it would explain this official document sentence:

The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface

The view hierarchy means the window system, any siblings of the SurfaceView means the normal views above and below the SurfaceView. In this case, the Window only render the normal views above the SurfaceView, and the document doesn’t tell you in order to make this mechanical works well, SurfaceView’s alpha channel was removed. Think about what if the SurfaceView can render alpha channel pixels, and the view below the SurfaceView still invisible, that’s can make the story flaw.

There is one exception when the SurfaceView setZOrderOnTop(true). Then the SurfaceView’s Surface is placed on top of Window’s surface, or you can imagine the SurfaceView’s image layer overlay above the Window’s image layer. Then it’s another story, the SurfaceView can render alpha channel pixels in this case, and the window needn’t hide any view now, because it’s below the SurfaceView’s image layer. And the z-order of the SurfaceView in the Window was ignored because the SurfaceView can insert into any depth without impact anything.

Let move to the next sentence.

This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

In order to understand the above sentence, you need to know how SurfaceFlinger works, it basically not so powerful when merging too image layers, in normal cases without SurfaceView, it would only send the different layers with its coordinates to the HWComposer, that’s the ideal work scene without doing any pixels combination or in another term “Blinding”. And also the SurfaceView was always being used to consume continuous image streams, so the alpha channel blended composite, with the view’s pixels on top of the SurfaceView, would be time-consuming.

Now, How about two SurfaceViews overlays together in the same view hierarchy? It’s setZOrderMediaOverlay(boolean isMediaOverlay) came to play. I left this as a question to the readers with the following hints. Surface’s Z-order was different than the SurfaceView’s Z-order, in order to tell the SurfaceFlinger which Surface is in front of, you need to config the SurfaceView with setZOrderMediaOverlay, then it’s Surface would use the Z-order of himself in the view hierarchy to determine which Surface is in front. But they are also below the Window’s Surface anyway, and if you set Both SurfaceViews’ setZOrderOnTop(True) and setZOrderMediaOverlay(True), then I guess you would blow the SurfaceFlinger system, I never tried this case.

--

--