AndroidQ 图形系统(10)SurfaceView实现原理之surface创建和绘制原理

上一篇文章说了SurfaceView默认Z-order是小于主窗口的,为了能够显示出来,需要以自身所占矩形区域在主窗口设置透明区域,这是在SurfaceView的回调onAttachedToWindow中实现的,本篇接着看SurfaceView另一个回调onWindowVisibilityChanged

首先还是贴出上一篇分析的ViewRootImpl的performTraversals方法部分代码:

private void performTraversals() {
	final View host = mView;
	......
	if (mFirst) {
		...
		host.dispatchAttachedToWindow(mAttachInfo, 0);
		...
	}
	...
	if (viewVisibilityChanged) {
		...
		host.dispatchWindowVisibilityChanged(viewVisibility);
		...
	}
	.....
	performMeasure();
	...
	performLayout();
	...
	performDraw();
	...
}

其实这里调用host.dispatchWindowVisibilityChanged和前面host.dispatchAttachedToWindow流程都类似的,就是遍历View树,让所有View都能执行到dispatchWindowVisibilityChanged,但我们一般不会重写View的dispatchWindowVisibilityChanged方法的,而是重写onWindowVisibilityChanged方法:
View.java

/**
     * Dispatch a window visibility change down the view hierarchy.
     * ViewGroups should override to route to their children.
     *
     * @param visibility The new visibility of the window.
     *
     * @see #onWindowVisibilityChanged(int)
     */
    public void dispatchWindowVisibilityChanged(@Visibility int visibility) {
        onWindowVisibilityChanged(visibility);
    }

所以这个遍历流程我们就不看了,直接来到SurfaceView的具体实现中:

SurfaceView.onWindowVisibilityChanged

   @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mWindowVisibility = visibility == VISIBLE;
        updateRequestedVisibility();
        updateSurface();
    }

首先调用父类View的onWindowVisibilityChanged,接着将初始化mWindowVisibility的值,代表当前SurfaceView是否可见,接着updateRequestedVisibility更新mRequestedVisible的值,我们重点要看的是updateSurface方法,这个方法中会创建SurfaceView自己的surface

updateSurface

/** @hide */
    protected void updateSurface() {
        ...
        ViewRootImpl viewRoot = getViewRootImpl();
        ...

        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
         
        final float alpha = getFixedAlpha();
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean visibleChanged = mVisible != mRequestedVisible;
        final boolean alphaChanged = mSurfaceAlpha != alpha;
        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
                && mRequestedVisible;
        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
        boolean redrawNeeded = false;

        if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha
                && alphaChanged) || windowVisibleChanged) {
            getLocationInWindow(mLocation);

            ...

            try {
                final boolean visible = mVisible = mRequestedVisible;
                mWindowSpaceLeft = mLocation[0];
                mWindowSpaceTop = mLocation[1];
                mSurfaceWidth = myWidth;
                mSurfaceHeight = myHeight;
                mFormat = mRequestedFormat;
                mLastWindowVisibility = mWindowVisibility;

                mScreenRect.left = mWindowSpaceLeft;
                mScreenRect.top = mWindowSpaceTop;
                mScreenRect.right = mWindowSpaceLeft + getWidth();
                mScreenRect.bottom = mWindowSpaceTop + getHeight();
               
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);

                if (creating) {
                    viewRoot.createBoundsSurface(mSubLayer);
                    mSurfaceSession = new SurfaceSession();
                    mDeferredDestroySurfaceControl = mSurfaceControl;

                    updateOpaqueFlag();
                    final String name = "SurfaceView - " + viewRoot.getTitle().toString();

                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                        .setName(name)
                        .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                        .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                        .setFormat(mFormat)
                        .setParent(viewRoot.getSurfaceControl())
                        .setFlags(mSurfaceFlags)
                        .build();
                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
                        .setName("Background for -" + name)
                        .setOpaque(true)
                        .setColorLayer()
                        .setParent(mSurfaceControl)
                        .build();

                } else if (mSurfaceControl == null) {
                    return;
                }

                boolean realSizeChanged = false;

                mSurfaceLock.lock();
                try {
                    mDrawingStopped = !visible;

                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                            + "Cur surface: " + mSurface);

                    SurfaceControl.openTransaction();
                    try {
                        mSurfaceControl.setLayer(mSubLayer);

                        if (mViewVisibility) {
                            mSurfaceControl.show();
                        } else {
                            mSurfaceControl.hide();
                        }
                        updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
                        if (mUseAlpha) {
                            mSurfaceControl.setAlpha(alpha);
                            mSurfaceAlpha = alpha;
                        }

                        ...
                        if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
                                    0.0f, 0.0f,
                                    mScreenRect.height() / (float) mSurfaceHeight);
                            ...
                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                        }
                        mSurfaceControl.setCornerRadius(mCornerRadius);
                        if (sizeChanged && !creating) {
                            mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
                        }
                    } finally {
                        SurfaceControl.closeTransaction();
                    }

                    if (sizeChanged || creating) {
                        redrawNeeded = true;
                    }

                    mSurfaceFrame.left = 0;
                    mSurfaceFrame.top = 0;
                    if (translator == null) {
                        mSurfaceFrame.right = mSurfaceWidth;
                        mSurfaceFrame.bottom = mSurfaceHeight;
                    } else {
                        float appInvertedScale = translator.applicationInvertedScale;
                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
                    }

                    final int surfaceWidth = mSurfaceFrame.right;
                    final int surfaceHeight = mSurfaceFrame.bottom;
                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
                            || mLastSurfaceHeight != surfaceHeight;
                    mLastSurfaceWidth = surfaceWidth;
                    mLastSurfaceHeight = surfaceHeight;
                } finally {
                    mSurfaceLock.unlock();
                }

                try {
                    redrawNeeded |= visible && !mDrawFinished;

                    SurfaceHolder.Callback[] callbacks = null;

                    final boolean surfaceChanged = creating;
                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                            
                            if (mSurface.isValid()) {
                                mSurface.forceScopedDisconnect();
                            }
                        }
                    }

                    if (creating) {
                        mSurface.copyFrom(mSurfaceControl);
                    }

                    if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
                            < Build.VERSION_CODES.O) {
                        
                        mSurface.createFrom(mSurfaceControl);
                    }

                    if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;
                            
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }
                        if (redrawNeeded) {
                           
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }

                            mPendingReportDraws++;
                            viewRoot.drawPending();
                            SurfaceCallbackHelper sch =
                                    new SurfaceCallbackHelper(this::onDrawFinished);
                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (mSurfaceControl != null && !mSurfaceCreated) {
                        mSurface.release();
                        releaseSurfaces();
                    }
                }
            } catch (Exception ex) {
                Log.e(TAG, "Exception configuring surface", ex);
            }
           
        } else {
           
            final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
                    || mWindowSpaceTop != mLocation[1];
            final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
                    || getHeight() != mScreenRect.height();
            if (positionChanged || layoutSizeChanged) { // Only the position has changed
                mWindowSpaceLeft = mLocation[0];
                mWindowSpaceTop = mLocation[1];
                
                mLocation[0] = getWidth();
                mLocation[1] = getHeight();

                mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
                        mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);

                if (translator != null) {
                    translator.translateRectInAppWindowToScreen(mScreenRect);
                }

                if (mSurfaceControl == null) {
                    return;
                }

                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
                    try {
                        
                        setParentSpaceRectangle(mScreenRect, -1);
                    } catch (Exception ex) {
                        Log.e(TAG, "Exception configuring surface", ex);
                    }
                }
            }
        }
    }

删了一些代码,看着代码挺多,其实很多代码都是获取SurfaceView的信息相关,宽高,透明度,坐标等,还有很多是surface创建之后需要设置的信息,可以看到有很多setXXX方法,这个方法的核心逻辑还是创建SurfaceControlSurface,创建方式和APP一样的,APP的SurfaceControlSurface是在ViewRootImpl的performTraversals方法中创建的,等下再说,先来看这个方法。

首先拿到SurfaceView宽高,可以通过外部调用setFixedSize设置,如果没设置就拿SurfaceView测量到的,这里提一下,如果SurfaceView的宽高测量模式为EXACTLY,则setFixedSize不会生效,这是SurfaceView自定义的测量方法onMeasure中调用了View的resolveSizeAndState决定的,具体可以自己去看,很简单,接着就是获取SurfaceView一系列信息,透明度,格式是否改变,可见性是否改变等等,这些值作为是否往下走的判断依据。

getLocationInWindow获取SurfaceView左上角坐标,接着一系列赋值,SurfaceView坐标啊,宽高,格式等,重点是SurfaceControl的创建,我在AndroidQ 图形系统(1)Surface与SurfaceControl创建分析已经分析过APP的Surface与SurfaceControl创建过程,流程都一样,总结一下:

  1. 首先应用进程会new一个java层SurfaceControl,什么都没做,然后传递到WMS进程,因为SurfaceControl在AIDL中是out类型,所以在WMS进程赋值。
  2. WMS在创建java层SurfaceControl的同时通过nativeCreate方法到native层做一系列初始化。
  3. SurfaceComposerClientcreateSurfaceChecked函数中通过ISurfaceComposerClient的Bp端mClient向SurfaceFlinger进程请求创建Surface,即调用createSurface函数,而在SurfaceFlinger进程Surface对应的是Layer
  4. 在第一次创建Layer的子类BufferQueueLayer的过程中,即在BufferQueueLayer的onFirstRef函数中会创建生产者消费者模型架构。
  5. SurfaceFlinger进程的任务完成之后会直接new一个SurfaceControl,并将SurfaceFlinger进程创建的Layer引用和生产者保存到SurfaceControl中,最后将native层SurfaceControl指针保存到java层SurfaceControl。
  6. native层SurfaceControl创建好了之后就可以通过此对象创建native层的Surface对象,即调用mSurface.copyFrom(mSurfaceControl),最后将native层Surface指针保存到java层Surface,最终java层和native层的Surface和SurfaceControl都创建完毕。

SurfaceView中创建surface也是同样的流程,大部分事情都是在native层做的,最重要的就是创建了SurfaceFlinger进程的生产者-消费者模式架构以及Layer。

SurfaceControl创建好了之后会调用很多setXXX方法,如setLayer,setAlpha,setPosition,setBufferSize等,这些信息最终都会设置到SurfaceFlinger的Layer中去,对于应用层来说其实就是SurfaceView的信息。

接着来看:

           if (creating) {
                        mSurface.copyFrom(mSurfaceControl);
                    }

通过copyFrom会到native层创建native层的Surface,并将指针保存在java层Surface中,这个方法执行完也宣告SurfaceView的Surface创建完毕。

再来看:

if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;

                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }

getSurfaceCallbacks会获取所有添加的SurfaceHolder.Callback,遍历他们调用surfaceCreated回调方法通知开发者SurfaceView的Surface创建完毕,我们可以拿到Surface进行绘制了。

再看下surfaceChanged回调的代码:

if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "surfaceChanged -- format=" + mFormat
                                    + " w=" + myWidth + " h=" + myHeight);
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }

上面这几个条件都会引起surfaceChanged

接着看surfaceDestroyed的回调,主要就是SurfaceView变得不可见时调用:

if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                            
                            if (mSurface.isValid()) {
                                mSurface.forceScopedDisconnect();
                            }
                        }
                    }

到此SurfaceView自己的Surface创建流程以及SurfaceHolder.Callback回调时机已经清楚了。这些完成之后就会对SurfaceView进行测量,布局与绘制了,虽然SurfaceView不会走onDraw流程,但是它重写了View的dispatchDraw方法,所以还是会调dispatchDraw这个方法:

SurfaceView.dispatchDraw

@Override
    protected void dispatchDraw(Canvas canvas) {
        if (mDrawFinished && !isAboveParent()) {
            // draw() is not called when SKIP_DRAW is set
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                // punch a whole in the view-hierarchy below us
                clearSurfaceViewPort(canvas);
            }
        }
        super.dispatchDraw(canvas);
    }

    private void clearSurfaceViewPort(Canvas canvas) {
        if (mCornerRadius > 0f) {
            canvas.getClipBounds(mTmpRect);
        
            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
                    mCornerRadius, mCornerRadius, mRoundedViewportPaint);
        } else {
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
    }

这个方法最终其实是调用了canvas.drawColor给SurfaceView画了一个黑色背景,这里的canvas是APP的,并不是SurfaceView的。

接着我们要来看看SurfaceView的绘制原理,首先来看一段最简单的使用SurfaceView绘制的代码:

package com.example.surfaceviewdemo;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainActivity extends AppCompatActivity {
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private Paint mPaint;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSurfaceView = findViewById(R.id.surfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new MyHolderCallback());
        mPaint = new Paint();
    }
    class MyHolderCallback implements SurfaceHolder.Callback{

        @Override
        public void surfaceCreated(final SurfaceHolder holder) {
            Log.d("dongjiao","surfaceCreated....");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mPaint.setColor(Color.RED);
                    Canvas canvas = holder.lockCanvas();
                    canvas.drawLine(0,100,400,100,mPaint);
                    holder.unlockCanvasAndPost(canvas);
                }
            }).start();
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Log.d("dongjiao","surfaceChanged....");
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            Log.d("dongjiao","surfaceDestroyed....");
        }
    }
}

在子线程中绘制一条直线:
AndroidQ 图形系统(10)SurfaceView实现原理之surface创建和绘制原理_第1张图片
我们可以发现好像用SurfaceView进行绘制和用普通View绘制的区别不大,SurfaceView除了能够在子线程绘制外其他都差不多,为什么在SurfaceView中需要自己手动获取canvas,而普通View不需要,想一下普通的自定义View的onDraw方法中的canvas哪来的,来自ViewRootImpl中的drawSoftware方法(非硬件加速情况):

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
	......
	canvas = mSurface.lockCanvas(dirty);
	....
	mView.draw(canvas);
	....
	surface.unlockCanvasAndPost(canvas);
	.....
}

我们可以发现,普通View的canvas和SurfaceView的canvas获取方式是一样的,不一样的只是Surface,普通View由系统帮我们获取之后通过DecorView传递到View树每一个View的onDraw方法中,因为SurfaceView有自己的Surface,所以需要手动通过自己的Surface获取自己单独的canvas,我们接下来就来看看SurfaceView中这小小的一段绘制代码到底做了什么事。

绘制的代码就只有三句:

               Canvas canvas = holder.lockCanvas();
               canvas.drawLine(0,100,400,100,mPaint);
               holder.unlockCanvasAndPost(canvas);

首先来看canvas的获取:

holder.lockCanvas

@Override
        public Canvas lockCanvas() {
            return internalLockCanvas(null, false);
        }

接着会调用SurfaceHolder的internalLockCanvas方法:

SurfaceHolder.internalLockCanvas

private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
            mSurfaceLock.lock();

            Canvas c = null;
            if (!mDrawingStopped && mSurfaceControl != null) {
                try {
                    if (hardware) {
                        c = mSurface.lockHardwareCanvas();
                    } else {
                        c = mSurface.lockCanvas(dirty);
                    }
                } catch (Exception e) {
                   
                }
            }

            if (c != null) {
                mLastLockTime = SystemClock.uptimeMillis();
                return c;
            }

            ......
            mSurfaceLock.unlock();

            return null;
        }

我们这里调用没有使用硬件加速(使用CPU渲染)的绘制,所以走mSurface.lockCanvas,dirty传递的是null,接着调到Surface的lockCanvas方法

Surface.lockCanvas

public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mLockedObject != 0) {
                
                throw new IllegalArgumentException("Surface was already locked");
            }
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

mLockedObject是native层返回的Surface的指针,是根据mNativeObject重新创建的一个native层Surface,接着来看看nativeLockCanvas方法:

nativeLockCanvas

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
   ......
    Rect dirtyRect(Rect::EMPTY_RECT);
    Rect* dirtyRectPtr = NULL;

    if (dirtyRectObj) {
        dirtyRect.left   = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
        dirtyRect.top    = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
        dirtyRect.right  = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
        dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
        dirtyRectPtr = &dirtyRect;
    }

    ANativeWindow_Buffer outBuffer;
    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
    if (err < 0) {
        const char* const exception = (err == NO_MEMORY) ?
                OutOfResourcesException :
                "java/lang/IllegalArgumentException";
        jniThrowException(env, exception, NULL);
        return 0;
    }

    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                         convertPixelFormat(outBuffer.format),
                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);

    SkBitmap bitmap;
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setInfo(info, bpr);
    if (outBuffer.width > 0 && outBuffer.height > 0) {
        bitmap.setPixels(outBuffer.bits);
    } else {
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    }

    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(bitmap);

    if (dirtyRectPtr) {
        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
    }

    if (dirtyRectObj) {
        env->SetIntField(dirtyRectObj, gRectClassInfo.left,   dirtyRect.left);
        env->SetIntField(dirtyRectObj, gRectClassInfo.top,    dirtyRect.top);
        env->SetIntField(dirtyRectObj, gRectClassInfo.right,  dirtyRect.right);
        env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
    }

    // Create another reference to the surface and return it.  This reference
    // should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,
    // because the latter could be replaced while the surface is locked.
    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    return (jlong) lockedSurface.get();
}

首先如果我们调用lockCanvas时指定了绘制的脏区域(对于非脏区域就直接复制上一个缓冲区的),则将此区域大小赋值给dirtyRect,让dirtyRectPtr指向此区域,这里我们传递的是null所以不会走这一步,接下来就是最重要的步骤了surface->lock,传递了两个参数,outBuffer类型为ANativeWindow_Buffer,其实可以理解为它就是代表GraphicBuffer,来看看ANativeWindow_Buffer结构体:

typedef struct ANativeWindow_Buffer {
    /// The number of pixels that are shown horizontally.
    int32_t width;

    /// The number of pixels that are shown vertically.
    int32_t height;

    /// The number of *pixels* that a line in the buffer takes in
    /// memory. This may be >= width.
    int32_t stride;

    /// The format of the buffer. One of AHardwareBuffer_Format.
    int32_t format;

    /// The actual bits.
    void* bits;

    /// Do not touch.
    uint32_t reserved[6];
} ANativeWindow_Buffer;

这个结构体中的宽高,格式等就是surface->lock申请到的GraphicBuffer的宽高,格式,最重要的是bits,它保存了GraphicBuffer的地址,GraphicBuffer中的数据比较大,在进程间传递是不现实的,所以传递的是句柄。

surface->lock主要目的就是向BufferQueue申请图形缓冲区GraphicBuffer,首次调用时GraphicBuffer将被分配并初始化为零,GraphicBuffer的内存分配使用的Gralloc,Gralloc中有两个重要的辅助,一个负责GraphicBuffer内存分配的GraphicBufferAllocator,另一个负责GraphicBuffer内存映射的GraphicBufferMapper

Surface->lock

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
	......
	status_t err = dequeueBuffer(&out, &fenceFd);
	......
	const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        const bool canCopyBack = (frontBuffer != nullptr &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);
        .....
	
	sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
	......
	
	void* vaddr;
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);
       .......
                
	if (res != 0) {
            err = INVALID_OPERATION;
        } else {
            mLockedBuffer = backBuffer;
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            outBuffer->bits   = vaddr;
        }

}

可以看一下,Surface->lock核心就是上面这点代码,这里用到了双重缓冲,我们申请的GraphicBuffer其实是backBuffer,还有一个frontBuffer正在显示,当我们在backBuffer绘制好数据之后调用unlockAndPost提交时会交换两个buffer,双重buffer使画面更加流畅。

dequeueBuffer函数最终会向BufferQueue申请GraphicBuffer,可参考AndroidQ 图形系统(3)dequeueBuffer函数分析,
申请的大致步骤是:优先获取已经绑定了GraphicBuffer且状态为FREE的BufferSlot,如果没有,再获取没有绑定GraphicBuffer且状态为FREE的BufferSlot,并设置flag为BUFFER_NEEDS_REALLOCATION,然后会通过GrallocGraphicBuffer进行内存分配和映射。

接着dequeueBuffer申请到GraphicBuffer之后,通过lockAsync最终调到GraphicBufferMapper中得到GraphicBuffer的vaddr,最后给开始传递过来的ANativeWindow_Buffer outBuffer赋值,outBuffer就保存了GraphicBuffer的相关信息,并且有了vaddr之后就可以对GraphicBuffer进行操作了。

我们再回到nativeLockCanvassurface->lock之后的操作,创建了SkImageInfoSkBitmap,这两个是Skia 2D图形库相关的东西,并且关联上了ANativeWindow_Buffer,然后通过java层canvas得到native层canvas,并调用setBitmapSkBitmap设置到canvas中,所以我们View中使用canvas API时其实使用的是Skia库画在了SkBitmap上,最终到了GraphicBuffer,目前Android系统已经很少使用Skia绘图了,更多的是OpenGL ES 或 Vulkan 。
再接着看Surface->lock最后一点代码,因为我们没有指定脏区域,所以这部分代码不会走,最后根据nativeObject得到的surface在拷贝一个lockedSurface返回给java层。

到此SurfaceHolder的lockCanvas函数已经分析完毕了,进行一下总结:

  1. SurfaceHolder的lockCanvas会通过java Surface的lockCanvas调到native层。
  2. 在native层最重要的一件事情就是通过native Surface的lock函数,最终通过dequeueBuffer函数向BufferQueue申请GraphicBuffer,首次申请GraphicBuffer是一件很复杂的事情,需要Gralloc模块帮忙分配内存和映射内存。
  3. GraphicBuffer申请完成之后会创建Skia库相关的SkImageInfoSkBitmapSkImageInfo的相关信息就是从申请到的GraphicBuffer中获取的,通过setInfoGraphicBufferSkBitmap关联,同时还将SkBitmap设置到Canvas中,这样上层java使用Canvas API底层依靠的是Skia进行绘制。

再回到SurfaceView中的一小段绘制代码:

               Canvas canvas = holder.lockCanvas();
               canvas.drawLine(0,100,400,100,mPaint);
               holder.unlockCanvasAndPost(canvas);

lockCanvas结束之后,使用Canvas APIdrawLine方法进行绘制,底层通过Skia完成绘制之后调用unlockCanvasAndPost将绘制结果提交,我们再来简单看看unlockCanvasAndPost方法,这个方法会同样是调到native层:

nativeUnlockCanvasAndPost

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    if (!isSurfaceValid(surface)) {
        return;
    }

    // detach the canvas from the surface
    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(SkBitmap());

    // unlock surface
    status_t err = surface->unlockAndPost();
    if (err < 0) {
        doThrowIAE(env);
    }
}

首先做一些收尾工作然后调用surface->unlockAndPost():

Surface->unlockAndPost

status_t Surface::unlockAndPost()
{
    if (mLockedBuffer == nullptr) {
        ALOGE("Surface::unlockAndPost failed, no locked buffer");
        return INVALID_OPERATION;
    }

    int fd = -1;
    status_t err = mLockedBuffer->unlockAsync(&fd);
    ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);

    err = queueBuffer(mLockedBuffer.get(), fd);
    ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
            mLockedBuffer->handle, strerror(-err));

    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = nullptr;
    return err;
}

可以看到此函数中调用了queueBuffer函数,这个函数会将绘制好的图形缓冲区放入BufferQueue中,然后通知SurfaceFlinger取,
AndroidQ 图形系统(4)queueBuffer函数分析
,通知的过程就是请求一个SurfaceFlinger进程的Vsync,待收到Vsync之后就可以拿到图形缓冲区进行合成了。

此函数最后mPostedBuffer = mLockedBuffer将backBuffer交换到前台。

到此SurfaceView的绘制原理大致流程已经分析完毕。

你可能感兴趣的:(Android图形系统)