窗口显示原理之Surface的创建与渲染

前言

  Activity、Dialog、PopupWindow等窗口显示时都会调用到WindowManager.addView(),而该方法最终又调用到了ViewRootImpl.setView()。所以ViewRootImpl.setView()可以视为窗口显示的入口,它完成了Surface的创建与渲染。
  本文尽可能地使用图形来简化Surface的创建与渲染的流程。想要了解更多细节的同学们请自行查阅文末的参考文献。

ViewRootImpl

  一个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个问题的答案都将在下文中揭晓。

Surface的创建

建立连接

Surface创建图1

图1-1

  上图中涉及到了进程间通信,那自然就离不开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的简化版。
在这里插入图片描述

图1-2

创建图层

在这里插入图片描述

图2-1

在这里插入图片描述

图2-2

  当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;
    ...
}


在这里插入图片描述

图2-3

创建Native层的Surface

在这里插入图片描述

图3-1

  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;
}

Surface的序列化与反序列化

  图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的关系如下图所示。
在这里插入图片描述

图4-1

Surface的渲染

  图1-2演示了APP进程与SurfaceFlinger进程建立的连接,该图可以再简化成图5-1。

在这里插入图片描述

图5-1

  每一个APP进程与SurfaceFlinger进程在内核空间中都会共享一块内存,这块内存就是被称为SharedClient的匿名共享内存块。SharedClient的作用就是用来传递UI元数据的。
在这里插入图片描述

图5-2

在这里插入图片描述

图5-3

  关于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

  在图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服务的关系概述和学习计划

发布了37 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/jiejingguo/article/details/102828820
今日推荐