ViewRootImpl、Surface、SurfaceFlinger简介

Activity 从加载布局文件到显示的过程分析
ViewManager、ViewRootImpl、WindowManagerImpl、WindowSession等详细解析

在上面两篇文章中我们可以知道,Window的顶层View为DecorView,DecorView、LayoutParams以及对应的ViewRootImpl保存在一个全局单例WindowMangerGlobal中,ViewRootImpl通过W和WindowSession与WindowManagerService进行通信。

下面的图片转载自网络

这里写图片描述

这里写图片描述

那么DecorView是如何传递给WindowMangerService的?然后又是如何显示到屏幕上的呢?先来看看View是如何绘制的。

View的绘制

ViewRootImpl的setView函数中,会调用requestLayout()函数,它会调用scheduleTraversals()函数,来看看
scheduleTraversals()函数是如何实现的

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //post一个mTraversalRunnable
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

其中post了一个mTraversalRunnable,那看看mTraversalRunnable里面做了什么

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

TraversalRunnable的run()调用doTraversal()函数,doTraversal()函数大家应该比较熟悉吧!很多介绍View的绘制流程都是从ViewRootImpl的doTraversal()开始介绍的。我们继续往下看

   void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

调用performTraversals()函数

public void performTraversals(){
    //省略若干代码
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    //省略若干代码
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    //省略若干代码
    performDraw();
}

在performTraversals()函数中相继执行Measure、Layout、Draw三个流程,就是我们常说的View绘制的三个流程,本篇文章先不往详细说,performDraw()会调用draw()函数,我们来看看draw()函数

  private void draw(boolean fullRedrawNeeded) {
          //在ViewRootImpl初始化时吃初始化
          Surface surface = mSurface;
          //省略若干代码
        //脏区,需要重绘的区域
         final Rect dirty = mDirty;
         //省略若干代码
         if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
  }

我们这里先不说硬件加速,Surface是什么东西呢?

Surface


Surface是什么东西呢?我们可以理解为每一个Window对应一个Surface,DecorView及其子View的绘制都是在Surface上进行的。那么你可能会问View的onDraw()函数是在Canvas上进行的,怎么又是在Surface上进行的呢?我们接着看drawSoftware()函数

  private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        try {
            final int left = dirty.left;
            final int top = dirty.top;
            final int right = dirty.right;
            final int bottom = dirty.bottom;
            调用mSurface的lockCanvas获取到一块canvas
            canvas = mSurface.lockCanvas(dirty);

            if (left != dirty.left || top != dirty.top || right != dirty.right
                    || bottom != dirty.bottom) {
                attachInfo.mIgnoreDirtyState = true;
            }
            canvas.setDensity(mDensity);
        } catch (Surface.OutOfResourcesException e) {
            handleOutOfResourcesException(e);
            return false;
        } catch (IllegalArgumentException e) {
            mLayoutRequested = true;    // ask wm for a new surface next time.
            return false;
        }
        try {

            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }

            dirty.setEmpty();
            mIsAnimating = false;
            mView.mPrivateFlags |= View.PFLAG_DRAWN;

            if (DEBUG_DRAW) {
                Context cxt = mView.getContext();
            }
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;
                //调用mDecorView的draw进行View树的绘制
                mView.draw(canvas);
                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                if (!attachInfo.mSetIgnoreDirtyState) {
                    attachInfo.mIgnoreDirtyState = false;
                }
            }
        } finally {
            try {
                //释放Canvas并进行绘制
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) {
              return false;
            }
        }
        return true;
    }

通过mSurface.lockCanvas(dirty)和脏区确定需要重绘的区域,然后调用DecorView的draw()方法进行View树的绘制,然后调用surface.unlockCanvasAndPost(canvas)释放canvas并通知顶层绘制。

介绍一个Canvas的概念:Canvas是什么?根据SDK文档的介绍可知,画图需要“四大金刚”相互合作,这四大金刚是:

· Bitmap:用于存储像素,也就是画布。可把它当做一块数据存储区域。

· Canvas:用于记载画图的动作,比如画一个圆,画一个矩形等。Canvas类提供了这些基本的绘图函数。

· Drawing primitive:绘图基元,例如矩形、圆、弧线、文本、图片等。

· Paint:它用来描述绘画时使用的颜色、风格(如实线、虚线等)等。

在一般情况下,Canvas会封装一块Bitmap,而作图就是基于这块Bitmap的。前面说的画布,其实指的就是Canvas中的这块Bitmap。

上面介绍mSurface是在RootViewImpl的构造函数中初始化的,也就是说这个mSurface是出于应用的进程中,那么mSurface对象是如何传递给WindowManagerService的呢?

public class Surface implements Parcelable {}

Surface实现了Parcelable接口,通过Binder机制进行跨进程传递,那么mSurface是在哪里与WindowMangerService产生关联的呢?ViewRootImpl的relayoutWindow()

   private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

       //省略若干代码
        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
      //省略若干代码
    }

调用mWindowSession的relayout将mSurface传递过去,我们在来看看WindowSession是怎么处理的呢?

public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags,
            int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Configuration
                    outConfig,
            Surface outSurface) {

        int res = mService.relayoutWindow(this, window, seq, attrs,
                requestedWidth, requestedHeight, viewFlags, flags,
                outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outConfig, outSurface);

        return res;
    }

调用了WindowManagerService的relayoutWindow()方法

public int relayoutWindow(Session session,IWindow client,

           WindowManager.LayoutParams attrs, int requestedWidth,
           int requestedHeight, int viewVisibility, boolean insetsPending,
           Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
            Configuration outConfig, SurfaceoutSurface){
        //省略若干代码.....

 try {
         //win就是WinState,这里将创建一个本地的Surface对象
         Surfacesurface = win.createSurfaceLocked();
          if(surface != null) {
            //先创建一个本地surface,然后在outSurface的对象上调用copyFrom
           //将本地Surface的信息拷贝到outSurface中,
           outSurface.copyFrom(surface);
        //省略若干代码......

}

继续看看WindowState的createSurfaceLocked()方法

Surface createSurfaceLocked() {

   //省略若干代码......
   try {
     //mSurfaceSession就是在Session上创建的SurfaceSession对象
     //这里,以它为参数,构造一个新的Surface对象
        mSurface = new Surface(
                mSession.mSurfaceSession, mSession.mPid,
                 mAttrs.getTitle().toString(),
                 0, w, h, mAttrs.format, flags);
      }
         Surface.openTransaction();//打开一个事务处理
       //省略若干代码......
         Surface.closeTransaction();//关闭一个事务处理。关于事务处理以后再分析
       //省略若干代码......

}

在createSurfaceLocked()方法中创建了一个Surface对象,也就是从应用的进程中将mSurface信息传递到WindowManagerService所在的进程SystemServer中。

从网上转载一张图概况上面的调用堆栈

这里写图片描述

SurfaceFlinger


那么Surface又是如何显示到屏幕上去的呢?多个不同的Window对应不同的Surface,它们之间的关系又是由谁处理的呢?又是怎么处理的呢?这就不得不提SurfaceFlinger了。

在SurfaceFlinger中抽象了一个layer的概念,每个surface对应一个layer。

这里写图片描述

如上图显示,屏幕位于一个三维坐标系中,其中Z轴从屏幕内指向屏幕外。

编号为①②③的矩形块叫显示层(Layer)。每一层有自己的属性,例如颜色、透明度、所处屏幕的位置、宽、高等。除了属性之外,每一层还有自己对应的显示内容,也就是需要显示的图像。

在Android中,Surface系统工作时,会由SurfaceFlinger对这些按照Z轴排好序的显示层进行图像混合,混合后的图像就是在屏幕上看到的美妙画面了。这种按Z轴排序的方式符合我们在日常生活中的体验,例如前面的物体会遮挡住后面的物体。

Surface系统提供了三种属性,一共四种不同的显示层。简单介绍一下:

· 第一种属性是eFXSurfaceNormal属性,大多数的UI界面使用的就是这种属性。它有两种模式:

   1)Normal模式,这种模式的数据,是通过前面的mView.draw(canvas)画上去的。这也是绝大多数UI所采用的方式。

   2)PushBuffer模式,这种模式对应于视频播放、摄像机摄录/预览等应用场景。以摄像机为例,当摄像机运行时,来自Camera的预览数据直接push到Buffer中,无须应用层自己再去draw了。

· 第二种属性是eFXSurfaceBlur属性,这种属性的UI有点朦胧美,看起来很像隔着一层毛玻璃。

· 第三种属性是eFXSurfaceDim属性,这种属性的UI看起来有点暗,好像隔了一层深色玻璃。从视觉上讲,虽然它的UI看起来有点暗,但并不模糊。而eFXSurfaceBlur不仅暗,还有些模糊。

SurfaceFlinger通过按照Z轴排好序的显示层进行图像混合,混合之后调用OpenGL进行显示到屏幕。

转载一张网上的图进行说明

这里写图片描述

了解更多SufaceFlinger细节,推荐Android4.4深入浅出之SurfaceFlinger总体结构

参考文章:

第8章 深入理解Surface系统
Android4.4深入浅出之SurfaceFlinger总体结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值