Activity、Dialog、PopupWindow等窗口显示时都会调用到WindowManager.addView()
,而该方法最终又调用到了ViewRootImpl.setView()
。所以ViewRootImpl.setView()
可以视为窗口显示的入口,它完成了Surface的创建与渲染。
本文尽可能地使用图形来简化Surface的创建与渲染的流程。想要了解更多细节的同学们请自行查阅文末的参考文献。
一个ViewRootImpl就对应一个Surface。
ViewRootImpl.java
public final Surface mSurface = new Surface();
即创建ViewRootImpl时也会创建一个Surface,但是此时的mSurface只是一个空壳。因为一个Java层的Surface会引用一个Native层的Surface来传递UI数据,而mSurface目前还不存在这样一个对应的Native层的Surface。
ViewRootImpl.setView()
是窗口显示的入口。
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
res = mWindowSession.addToDisplay(mWindow, ...);
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
...
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
从代码中可以看到先执行了requestLayout()
,而这个方法又调用到了mChoreographer.postCallback(..., mTraversalRunnable, ...)
。方法名postCallback表明了该方法推送了一个回调,即mTraversalRunnable。
到这里,同学们一定有2个疑问:1、Native层的Surface什么时候创建的?2、回调接口mTraversalRunnable什么时候被调用的,它又做了哪些事?这2个问题的答案都将在下文中揭晓。
上图中涉及到了进程间通信,那自然就离不开Binder。在Surface创建过程中,APP进程与system_server进程的通信接口是IWindowSession,其中Binder实体为Session,Binder代理被ViewRootImpl.mWindowSession引用。system_server进程与SurfaceFlinger进程的通信接口是ISurfaceComposerClient,其中Binder实体为Client,Binder代理被SurfaceComposerClient.mClient引用。
图1-1演示了APP进程与SurfaceFlinger进程建立连接的过程。图1-2是图1-1的简化版。
当APP进程与SurfaceFlinger进程建立连接之后,APP进程会收到显示系统的时间脉冲,执行回调接口TraversalRunnable。在执行过程中,ViewRootImpl跨进程调用Session的relayout(),最后令SurfaceFlinger创建了图层BufferLayer和IGraphicBufferProducer对象。
IGraphicBufferProducer也是进程间的通信接口。从名字中就可以看出来,它负责生成图形缓冲区。其中Binder实体为MonitoredProducer,Binder代理被SurfaceControl.mGraphicBufferProducer引用。而MonitoredProducer中的接口实现则是委托给BufferQueueProducer来实现。
SurfaceComposerClient.cpp
status_t SurfaceComposerClient::createSurfaceChecked(
...,
sp<SurfaceControl>* outSurface,
...)
{
sp<SurfaceControl> sur;
...
if (mStatus == NO_ERROR) {
...
sp<IGraphicBufferProducer> gbp;
...
//对应上图中(16),执行下面代码后,gbp指针就会指向MonitoredProducer的代理对象
err = mClient->createSurface(name, w, h, format, flags, parentHandle,
windowType, ownerUid, &handle, &gbp);
...
if (err == NO_ERROR) {
//SurfaceControl创建成功, 指针赋值
*outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
}
}
return err;
}
SurfaceControl.cpp
/*
gbp指针赋值给了SurfaceControl.mGraphicBufferProducer,
所以SurfaceControl.mGraphicBufferProducer也指向了MonitoredProducer的代理对象
*/
SurfaceControl::SurfaceControl(
...,
const sp<IGraphicBufferProducer>& gbp,
...)
: ..., mGraphicBufferProducer(gbp), ...
{
}
BufferLayer.cpp
void BufferLayer::onFirstRef() {
...
BufferQueue::createBufferQueue(&producer, &consumer, true);
//对应上图中(19),MonitoredProducer只是一个装饰类,它的实际功能都委托给构造它的参数producer
mProducer = new MonitoredProducer(producer, mFlinger, this);
...
}
BufferQueue.cpp
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
...) {
...
//BufferQueueProducer才是IGraphicBufferProducer接口的真正实现者
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
...
*outProducer = producer;
...
}
Java层的Surface中最重要的变量就是mNativeObject,它保存了一个地址,该地址就是Native层的Surface对象的地址。
public class Surface implements Parcelable {
long mNativeObject;
}
创建Native层的Surface时,该Surface保存了MonitoredProducer的代理对象。
Surface.java
//对应图2-2中(22)
public void copyFrom(SurfaceControl other) {
...
long surfaceControlPtr = other.mNativeObject;
...
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
...
mNativeObject = ptr; // 对应图3-1中(25)mNativeObject指向native创建的Surface
}
android_view_Surface.cpp
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
//把java指针转化成native指针
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
//下面代码中ctrl->getSurface()对应图3-1中(23)
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner); //强引用
}
return reinterpret_cast<jlong>(surface.get());
}
SurfaceControl.cpp
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
return generateSurfaceLocked();
}
return mSurfaceData;
}
sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
/*
对应图3-1中的(24),SurfaceControl中的mGraphicBufferProducer指针变量传入
Surface中,所以Surface.mGraphicBufferProducer也指向了MonitoredProducer的代理对象
*/
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}
图3-1中(26),system_server进程中的临时Surface序列化后,里面的数据反序列化成APP进程的ViewRootImpl.mSurface的数据。要理解这个过程,就要先知道IWindowSession的接口定义。
interface IWindowSession {
int relayout(IWindow window, ..., out Surface outSurface);
}
在Surface参数前使用了out修饰。这说明数据只能从服务端流向客户端。我们来看看IWindowSession.Stub.Proxy怎么实现这个接口的。
public int relayout(android.view.IWindow window, int seq, android.view.WindowManager.LayoutParams attrs, ... ,
android.view.Surface outSurface) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//只把用in修饰的参数序列化进了_data中,outSurface使用out修饰,所以不传入_data
...
mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
...
if ((0 != _reply.readInt())) {
//从Parcel中取出数据,并赋值给APP端的Java层的Surface
outSurface.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
从代码来看App端的outSurface并没有传到_data里,也就是并没有传递给Server端,相反,它是从_reply这个Parcel里读出来的。
现在来看下Server端的处理,在IWindowSession.Stub的onTransact里
case TRANSACTION_relayout: {
...
android.view.Surface _arg15;
_arg15 = new android.view.Surface();//创建临时Surface
int _result = this.relayout(...);
reply.writeNoException();
reply.writeInt(_result);
...
if ((_arg15 != null)) {
reply.writeInt(1);
//将临时Surface写入reply
_arg15.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
_arg15这个参数就是Surface, 也就是图2-1的(10)中在system_server进程中创建的临时Surface.。最后将这个Surface写入到reply的Parcel中。
对AIDL中in、out、inout这些Tag不了解的同学可以参考下面文章:
你真的理解Android AIDL中的in,out,inout么?
Android:学习AIDL,这一篇文章就够了(上)
我们知道system_server进程的临时Surface引用了Native层的Surface,而Native层的Surface的mGraphicBufferProducer指向了MonitoredProducer的代理对象。那么APP进程中的ViewRootImpl.mSurface又该如何生成对应的Native层的Surface,并令该Native层的Surface的mGraphicBufferProducer也指向MonitoredProducer的代理对象呢?
从上面的代码可以知道,这些是通过surface.writeToParcel()
和surface.readFromParcel()
实现的。
Surface.java
public class Surface implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
if (dest == null) {
throw new IllegalArgumentException("dest must not be null");
}
synchronized (mLock) {
...
//将Native层的Surface地址序列化
nativeWriteToParcel(mNativeObject, dest);
}
...
}
public void readFromParcel(Parcel source) {
if (source == null) {
throw new IllegalArgumentException("source must not be null");
}
synchronized (mLock) {
...
/*
nativeReadFromParcel会读取Native层的Surface地址,
若当前进程没有该地址,就会创建一个Native层的Surface。
*/
setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
}
}
}
android_view_Surface.cpp
static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
doThrowNPE(env);
return;
}
/*
将Java层传来的地址,即nativeObject,转成Native层的地址。
并赋值给self,这样self就指向了Native层的Surface了
*/
sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
//创建Surface的临时变量
android::view::Surface surfaceShim;
if (self != nullptr) {
/*
将self.mGraphicBufferProducer赋值给surfaceShim.mGraphicBufferProducer,
这样surfaceShim.mGraphicBufferProducer也指向了MonitoredProducer的代理对象
*/
surfaceShim.graphicBufferProducer = self->getIGraphicBufferProducer();
}
//将surfaceShim序列化
surfaceShim.writeToParcel(parcel, /*nameAlreadyWritten*/true);
}
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
doThrowNPE(env);
return 0;
}
//创建临时变量
android::view::Surface surfaceShim;
/*
从parcel读取数据,这样surfaceShim.mGraphicBufferProducer就被赋值了,
指向MonitoredProducer的代理对象
*/
surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);
//将Java层传来的地址,即nativeObject,转成Native层的地址,并存入指针变量self
sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
/*
如果self不是空指针,也就是存在Native层的Surface,
且self.mGraphicBufferProducer与surfaceShim.mGraphicBufferProducer
指向同一个MonitoredProducer的代理对象,
就直接返回self指针,实际上就是把参数nativeObject回传回去了。
*/
if (self != nullptr
&& (IInterface::asBinder(self->getIGraphicBufferProducer()) ==
IInterface::asBinder(surfaceShim.graphicBufferProducer))) {
// same IGraphicBufferProducer, return ourselves
return jlong(self.get());
}
sp<Surface> sur;
if (surfaceShim.graphicBufferProducer != nullptr) {
/* 若当前进程没有Native层的Surface,或者存在Native层的Surface,
但是该Surface内的mGraphicBufferProducer指向的对象与surfaceShim.mGraphicBufferProducer不同。
那就新建一个Native层的Surface,并赋值surfaceShim.mGraphicBufferProducer。
*/
sur = new Surface(surfaceShim.graphicBufferProducer, true);
...
}
...
//返回新建的Native层的Surface
return jlong(sur.get());
}
经过序列化和反序列化之后,APP进程的Surface与system_server进程的Surface的关系如下图所示。
图1-2演示了APP进程与SurfaceFlinger进程建立的连接,该图可以再简化成图5-1。
每一个APP进程与SurfaceFlinger进程在内核空间中都会共享一块内存,这块内存就是被称为SharedClient的匿名共享内存块。SharedClient的作用就是用来传递UI元数据的。
关于SharedClient的内容,大家可以参考罗升阳的这篇文章:Android应用程序与SurfaceFlinger服务的关系概述和学习计划。下面引用里面的一段话。
在每一个SharedClient里面,有至多31个SharedBufferStack。字面上来看,SharedBufferStack就是共享缓冲区堆栈。怎么理解呢?首先,Shared表明这个堆栈是共享的。那么由谁来共享呢?当然就是Android应用程序和SurfaceFlinger服务了。其次,Buffer表明这个堆栈的内容是缓冲区。什么样的缓冲区呢?当然就是用来描述UI元数据的缓冲区了。再者,Stack表明用来描述UI元数据的缓冲区是需要按照一定的规则来访问的。综合起来,我们就可以认为每一个SharedBufferStack就是用来描述一系列需要按照一定规则来访问的缓冲区。
每一个SharedBufferStack都对应一个Surface,即一个窗口。这样,我们就可以知道为什么每一个SharedClient里面包含的是一系列SharedBufferStack而不是单个SharedBufferStack:一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含有多个窗口,即Surface。从这里也可以看出,一个Android应用程序至多可以包含31个Surface。
SharedBufferStack长什么样子呢?如下图所示。
在图5-3中,为了方便描述,我们假设图中的SharedBufferStack有5个Buffer,其中,Buffer-1和Buffer-2是已经使用了的,而Buffer-3、Buffer-4和Buffer-5是空闲的。指针head和tail分别指向空闲缓冲区列表的头部和尾部,而指针queue_head指向已经使用了的缓冲区列表的头部。从这里就可以看出,从指针tail到head之间的Buffer即为空闲缓冲区表,而从指针head到queue_head之间的Buffer即为已经使用了的缓冲区列表。注意,图中的5个Buffer是循环使用的。
空闲缓冲区比较好理解,接下来我们重点解释一下那些已经被使用了的缓冲区,即图5-3中的Buffer-1和Buffer-2。
前面我们说过,SharedBufferStack中的缓冲区只是用来描述UI元数据的,这意味着它们不包含真正的UI数据。真正的UI数据保存在GraphicBuffer中(GraphicBuffer从名字就可以看出是图形缓冲区的意思)。因此,为了完整地描述一个UI,SharedBufferStack中的每一个已经使用了的缓冲区都对应有一个GraphicBuffer,用来描述真正的UI数据。当SurfaceFlinger服务准备绘制Buffer-1和Buffer-2的时候,就会找到与它们所对应的GraphicBuffer,把里面的UI数据绘制到屏幕上。
做完以上铺垫,终于可以进入正题了——Surface是怎么渲染的。上文提到回调接口mTraversalRunnable被执行。
ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();//doTraversal()会调用到ViewRootImpl.performTraversals()
}
}
private void performTraversals() {
...
//relayoutWindow()内部调用了mWindowSession.relayout(),最终创建了Native层的Surface
relayoutWindow(params, viewVisibility, insetsPending);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
//performDraw()-->draw()-->drawSoftware()
performDraw();
...
}
performDraw()
最终会调用到drawSoftware()
,它正是Surface的渲染入口。
ViewRootImpl.java
private boolean drawSoftware(Surface surface, AttachInfo attachInfo,...) {
// Draw with software renderer.
final Canvas canvas;
...
canvas = mSurface.lockCanvas(dirty); //step 1
...
mView.draw(canvas); //setp 2
...
mSurface.unlockCanvasAndPost(canvas); //step 3
}
上面三个步骤大致地描述了Surface的渲染逻辑。
mSurface.lockCanvas(dirty),这个方法做了2件事:1、将画布Canvas设置成窗口的大小;2、找到对应的SharedBufferStack,并且从它的空闲缓冲区列表的尾部取出一个空闲的Buffer。请求SurfaceFlinger服务为这个Buffer分配一个图形缓冲区GraphicBuffer,这工作实际上就是由IGraphicBufferProducer对象完成的。
mView.draw(canvas),这个方法把View的UI数据绘制进Canvas中。Canvas最终产出一张位图Bitmap。
mSurface.unlockCanvasAndPost(canvas),这个方法将Canvas中Bitmap的UI数据写入图形缓冲区GraphicBuffer中,并通知SurfaceFlinger去绘制那些保存在已经使用了的缓冲区所描述的图形缓冲区GraphicBuffer。
最后我们来总结一下Surface的创建与渲染流程。ViewRootImpl.setView()
是窗口显示的入口,接着经过下面5个步骤,完成Surface的创建与渲染流程,从而绘制并显示了窗口。
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout(); //1、推送了一个回调接口mTraversalRunnable
...
res = mWindowSession.addToDisplay(mWindow, ...);//2、APP进程与SurfaceFlinger进程建立连接
...
}
//3、建立连接后,执行回调接口mTraversalRunnable,最终调用performTraversals()
private void performTraversals() {
...
//4、创建Native层的Surface
relayoutWindow(params, viewVisibility, insetsPending);
...
//5、绘制窗口
performDraw();
...
}
Surface的创建:
Android的UI显示原理之Surface的创建
Android Surface创建
Surface的渲染:
Android的UI显示原理之Surface的渲染
Android应用程序与SurfaceFlinger服务的关系概述和学习计划