关于图形显示部分可以参考官网:Graphics
图像显示原理
Linux通常使用Framebuffer来用作显示输出(Framebuffer知识详解在这里),Framebuffer就是一块内存区域,它通常是显示驱动的内部缓冲区在内存中的映射。
一旦用户进程把图像数据复制到Framebuffer中,显示驱动会一个像素一个像素地扫描整个Framebuffer,并根据其中的值更新屏幕上像素点的颜色。
驱动中这个更新屏幕的动作是固定的,它的周期就是我们常说的刷新率
Android关于图像渲染显示的架构图如下:

结合这张图,我们需要重点关注的是:
-
Native Framework中的Surface- 无论开发者使用什么渲染
API,一切内容都会渲染到Surface上 Surface中会关联一个BufferQueue用于提供图像数据缓存- 大多数客户端使用
OpenGL ES或Vulkan渲染到Surface - 有些客户端使用
Canvas渲染到Surface
- 无论开发者使用什么渲染
-
Image Stream Producer的定义是能够生成图形缓冲区以供消耗的任何对象。每个Producer都会关联一个Surface,例如-
Canvas 2D:Java层主要是通过View中创建的Surface对象来进行操作- 该
Surface对象会与SurfaceFlinger进行关联,并通过lockCanavas()接口获取Canvas对象 lockCanvas()会将CPU渲染程序连接到BufferQueue的生产方,直到Surface被销毁时才会断开连接
- 该
-
mediaserver视频解码器:通过MediaPlayer的setSurfaceHolder()接口与SurfaceView中的Surface进行绑定
-
-
对于
Image Stream Consumer来说,主要是SurfaceFlinger,该系统服务会消耗当前可见的Surface,并使用WindowManager中提供的信息将它们合成到显示部分。SurfaceFlinger是可以修改所显示部分内容的唯一服务。SurfaceFlinger使用OpenGL和Hardware Composer来合成一组Surface。- 当应用进入前台时,它会从
WindowManager请求缓冲区。然后,WindowManager会从SurfaceFlinger请求layer。layer是surface(包含BufferQueue)和SurfaceControl(包含屏幕框架等层元数据)的组合。
SurfaceFlinger创建layer并将其发送至WindowManager。- 然后,
WindowManager将Surface发送至应用,但会保留SurfaceControl来操控应用在屏幕上的外观。
-
Window Positioning中的WindowManager主要是用来控制Window对象Window对象是用来存放View对象的容器,每个Window对象都会关联Surface对象WindowManager监视Window对象的生命周期、输入和焦点事件、屏幕方向、转换、动画、位置、变换、z顺序等- 然后将所有
Window元数据发送给SurfaceFlinger,SurfaceFlinger利用这些元数据把自己管理的所有Surface组合成layer - 然后交给
Hardware Composer做进一步处理
-
HAL层中的Hardware Composer(HWC)会根据当前硬件来进一步进行缓冲区的组合,它的具体实现依赖于特定的显示设备- 官网关于
HWC的数据流如下:

SurfaceFlinger作为client向HWC提供一个完整的layer列表,然后询问HWC计划如何处理HWC会将这些layer标记为client合成或者device合成并告知SurfaceFlingerSurfaceFlinger将处理标记为client合成的layer,然后通过BufferQueue传递给HWC- 余下的
layer由HWC自行处理
- 官网关于
网上一篇很有趣的渲染总结(文中有些错误,但瑕不掩瑜):Android渲染原理
VSYNC信号
前面提到Linux使用Framebuffer来用作显示输出。但是,如果在屏幕更新到一半时,用户进程更新了Framebuffer中的数据,将导致屏幕上画面的上半部分是前一帧的画面,下半部分变成了新的画面。当然这种异常会在下次刷新时纠正过来,但是在用户感知上画面会出现闪烁感
- 针对这种情况,早期的解决方法是使用
双缓冲机制,双缓冲就是提供两块Framebuffer,一块用于显示,另一块用于数据更新,数据准备好后,通过ioctl操作告诉显示设备切换用于显示的Framebuffer,这样图像就能快速的显示出来了 - 但是双缓冲并没有完全解决问题,虽然双缓冲切换的速度很快,但是如果切换的时间点不对,在画面更新一半的时候进行切换,还是会出现单缓冲区遇到的闪烁问题
- 当然,可以在底层进行控制,当收到切换请求后内部并不马上执行,而是等到刷新完成后再切换,这样可以完全避免画面重叠的问题
- 但是,这样做会带来新的问题,如果
ioctl操作完成后缓冲区没有切换,应用就不能确定何时可以再使用缓存区,只能通过ioctl不停地查询缓冲区状态,直到切换完成。这种CPU主动查询的方式很低效
为此Android让底层固定地发送信号给用户进程,通知进程切换的时机,这样就避免了用户进程主动查询的操作。而这个信号就是VSYNC信号
官方传送门:
VSYNC
官方描述如下:VSYNC信号用来同步整个显示流程(Display Pipeline)。显示流程包括app渲染、SurfaceFlinger合成、HWC(硬件渲染)组成
(这部分感觉原文更容易理解)VSYNC synchronizes the time apps wake up to start rendering, the time SurfaceFlinger wakes up to composite the screen, and the display refresh cycle.
VSYNC信号应该由显示驱动产生,这样才能达到最佳效果。但是Android为了能运行在不支持VSYNC机制的设备上,也提供了用软件来模拟产生VSYNC信号的手段
官网描述:通过
HWC来产生VSYNC信号,并通过接口回调将事件进行发送(主要是SurfaceFlinger进行事件接收)
基础知识铺垫完成,我们先来看看Surface
Surface
官网对
Surface的描述是:A surface is an interface for a producer to exchange buffers with a consumer.
上面描述的是一种生产者-消费者的模式,而Surface充当了中间衔接的角色。
以Activity中UI显示为例:
-
生产者的任务就是把图形绘制在Surface对象上- 比较出名的生产者就是
SurfaceView组件了
- 比较出名的生产者就是
-
SurfaceFlinger作为消费者会把所有Surface对应的图像层混合在一起 -
最后
消费者将其输出到FrameBuffer中,这样在屏幕上就看到最后合成的图像了
下面我们从Java层开始分析Surface
应用中Surface的创建过程
应用开发中很少直接使用Surface,因为每个Activity中都已经创建好了各自的Surface对象,通常只有一些特殊的应用才需要在Activity之外再去创建Surface,例如相机、视频播放应用。
不过,通常这些应用也是通过创建SurfaceView来使用Surface
需要注意的是,在应用中不能直接去创建一个可用的Surface对象(也可以说直接创建出的对象没什么实际用途),因为这样创建出的Surface对象和SurfaceFlinger之间没有任何关联。
该如何创见一个可用的Surface对象呢?
我们看下Surface类的定义:
public class Surface implements Parcelable {
long mNativeObject;
// 一个无参构造,空实现
public Surface() {
}
public Surface(SurfaceTexture surfaceTexture) {
if (surfaceTexture == null) {
throw new IllegalArgumentException("surfaceTexture must not be null");
}
mIsSingleBuffered = surfaceTexture.isSingleBuffered();
synchronized (mLock) {
mName = surfaceTexture.toString();
setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
}
}
}
Surface类对外有两个构造方法:
-
一个是无参构造,实现也是空的
- 注释中说这个主要是给
readFromParcel()反序列化用的 - 那我们看下
readFromParcel()方法
public void readFromParcel(Parcel source) { if (source == null) { throw new IllegalArgumentException("source must not be null"); } synchronized (mLock) { mName = source.readString(); mIsSingleBuffered = source.readInt() != 0; setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); } } - 注释中说这个主要是给
-
另一个需要传递
SurfaceTexture对象作为参数- 这就复杂了,还要准备一个
SurfaceTexture对象
- 这就复杂了,还要准备一个
聪明的我们会发现,readFromParcel()和new Surface(SurfaceTexture surfaceTexture)都会执行一个setNativeObjectLocked()方法,我们看下方法实现:
private void setNativeObjectLocked(long ptr) {
if (mNativeObject != ptr) {
...
mNativeObject = ptr;
...
}
}
setNativeObjectLocked()方法很简单,只是更新了mNativeObject变量的数值,重点就是参数了:
setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
这两个setNativeObjectLocked()方法的调用从参数的命名来看是针对不同数据来源的处理。
看来要看下native的实现了,以nativeReadFromParcel()为例来看下:
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
...
android::view::Surface surfaceShim;
// 解析 Parcel 数据,并填充到 native层 的 Surface对象 surfaceShim
surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);
// 将传入的指针转换为 native层 的 Surface对象 self
sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
// 比对 surfaceShim 和 self 中的 Binder 对象 IGraphicBufferProducer
if (self != nullptr
&& (IInterface::asBinder(self->getIGraphicBufferProducer()) ==
IInterface::asBinder(surfaceShim.graphicBufferProducer))) {
// 判断是同一个 IGraphicBufferProducer ,直接返回当前指针
return jlong(self.get());
}
sp<Surface> sur;
if (surfaceShim.graphicBufferProducer != nullptr) {
// IGraphicBufferProducer 不同
// 且 surfaceShim 的 IGraphicBufferProducer 不为空
// 创建一个新的 Surface 对象 sur
sur = new Surface(surfaceShim.graphicBufferProducer, true);
sur->incStrong(&sRefBaseOwner);
}
...
// 将 sur 的指针返回给 Java 层
return jlong(sur.get());
}
到这里我们不难看出
Java层的Surface对象最重要的数据是mNativeObject变量mNativeObject是一个指针,指向的native层的Surface对象
native层在判断是否新建Surface对象的逻辑依赖的是IGraphicBufferProducer对象IGraphicBufferProducer对象是一个Binder引用对象
那么接下来我们重点就是这个IGraphicBufferProducer了。
我们先看下native层中Surface类的继承关系:
class Surface
: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
ANativeObjectBase的定义如下:
template <typename NATIVE_TYPE, typename TYPE, typename REF,
typename NATIVE_BASE = android_native_base_t>
class ANativeObjectBase : public NATIVE_TYPE, public REF
{
...}
整理成继承关系图就是:

再看下Surface的构造方法:
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
...
mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {
... // 初始化各种成员变量
}
从构造函数的参数可以看到,native层的Surface将IGraphicBufferProducer对象保存到了mGraphicBufferProducer变量中。
暂时还是不清楚mGraphicBufferProducer哪里来的,我们去WMS中看看
WMS中Surface的创建过程
此处要从Activity的onResume()生命周期说起
onResume()到WMS.relayoutWindow()
我们已经知道,当AMS触发onResume()生命周期时会调用到ActivityThread类的handleResumeActivity()方法,代码如下:
public void handleResumeActivity(...) {
...
// 此处会触发 onResume 声明周期回调
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
ViewManager wm = a.getWindowManager();
...// 省略很多 Window 处理逻辑
wm.addView(decor, l);
...
}
从方法中可以看到,执行完onResume()后调用了ViewManager的addView(decor, l)方法
知识点:在
onResume方法调用后才真正进行View的添加
ViewManager是一个接口类,真正的实现类是WindowManagerImpl,addView()方法实现也很简单:
public void addView(...) {
applyDefaultToken(params);
mGlobal.addView

本文详细剖析了Android图形显示过程中Surface的创建、管理及与SurfaceFlinger的交互,涉及Framebuffer、VSYNC信号、SurfaceFlinger、HardwareComposer等关键概念,阐述了图像数据从应用到屏幕显示的完整流程。
最低0.47元/天 解锁文章
2008

被折叠的 条评论
为什么被折叠?



