我是靠谱客的博主 昏睡洋葱,最近开发中收集的这篇文章主要介绍Activity显示三部曲(三)——ViewRootImpl、Surface、SurfaceFlinger简介,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

在这里插入图片描述
那么DecorView是如何传递给WindowMangerService的?然后又是如何显示到屏幕上的呢?先来看看View是如何绘制的。

View的绘制

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

//ViewRootImpl
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        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();
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
         return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
 int desiredWindowHeight) {

    final View host = mView;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

private void performDraw() {
	Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
	boolean canUseAsync = draw(fullRedrawNeeded);
	Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

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

  private void draw(boolean fullRedrawNeeded) {
          //在ViewRootImpl初始化
          Surface surface = mSurface;
         //省略若干代码
         if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
             return;
         }
  }

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。

public final Surface mSurface = new Surface();

上面介绍mSurface是在RootViewImpl创建的,也就是说这个mSurface是出于应用的进程中,那么mSurface对象是如何传递给WindowManagerService的呢?

public class Surface implements Parcelable {}

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

//performTraversals()
   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是怎么处理的呢?

//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()方法

//WindowManagerService
 public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
		int requestedWidth, int requestedHeight, int viewVisibility, int flags,
		long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
		Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
        DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
		Surface outSurface) {
			
	int result = 0;

	 result = createSurfaceControl(outSurface, result, win, winAnimator);
}

private int createSurfaceControl(Surface outSurface, int result, WindowState win,
		WindowStateAnimator winAnimator) {

	WindowSurfaceController surfaceController;
	try {
		Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
		surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
	} finally {
		Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
	}
	if (surfaceController != null) {
		surfaceController.getSurface(outSurface);
	}
	outSurface.release();
	return result;
}

进而进入WindowStateAnimator.createSurfaceLocked()方法中

//WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {

	if (mSurfaceController != null) {
		return mSurfaceController;
	}

	mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
		attrs.getTitle().toString(), width, height, format, flags, this,
		windowType, ownerUid);

	return mSurfaceController;
}

//WindowSurfaceController.java
void getSurface(Surface outSurface) {
		outSurface.copyFrom(mSurfaceControl);
}

以上也就是从应用进程中将mSurface信息传递到WindowManagerService所在的SystemServer进程中。

SurfaceFlinger

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

在SurfaceFlinger中抽象了一个layer的概念,每个surface对应一个layer。
在这里插入图片描述
如上图显示,屏幕位于一个三维坐标系中,其中Z轴从屏幕内指向屏幕外。

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

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

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

  • 第一种属性是eFXSurfaceNormal属性,大多数的UI界面使用的就是这种属性;
  • 第二种属性是eFXSurfaceBlur属性,这种属性的UI有点朦胧美,看起来很像隔着一层毛玻璃;
  • 第三种属性是eFXSurfaceDim属性,这种属性的UI看起来有点暗,好像隔了一层深色玻璃。从视觉上讲,虽然它的UI看起来有点暗,但并不模糊。而eFXSurfaceBlur不仅暗,还有些模糊;

而第一种属性有两种模式:

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

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

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

转载一张网上的图进行说明:
在这里插入图片描述

最后

以上就是昏睡洋葱为你收集整理的Activity显示三部曲(三)——ViewRootImpl、Surface、SurfaceFlinger简介的全部内容,希望文章能够帮你解决Activity显示三部曲(三)——ViewRootImpl、Surface、SurfaceFlinger简介所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部