Android Render(二)7.1源碼硬件加速下draw繪制流程分析


閱讀者三篇Android繪制文章,會讓你對理解Android繪制有幫助:


分析從draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)兩個方法入手:

draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)方法從表面看來就是接受的參數不一樣, 其實二期有一個先后順序,從屬關系,但是也要分情況,就看當前的View是不是頂級的DecorView了,就是說一個View有沒有parent view,View的兩個draw方法是有不一樣的調用順序的,當然只有DecorView是頂級的View,DecorView沒有parent view。

正常情況下,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法被當前view的parentView調用,在draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中會根據是否支持硬件加速來走不通的流程最終都會調用到draw(Canvas canvas)方法來做正在的繪制,draw(Canvas canvas)方法中會調用到dispatchDraw(canvas)方法,來向下分發繪制,dispatchDraw(canvas)方法中會調用draw(Canvas canvas, ViewGroup parent, long drawingTime)。繪制就層層向下傳遞。

但是作為頂級的DecorView就不同了,ViewRootImpl調用DecorView的draw(Canvas canvas)方法直接開啟整個view tree的繪制,DecorView的draw(Canvas canvas)方法中調用dispatchDraw(canvas)開始向下分發繪制。一層層傳到到view tree的最底層。

圖1 View Hierarchy

整個View Hierarchy真正繪制開始是從DecorView的draw(Canvas canvas)方法開始的,下面描述一下Activity的啟動到調用到DecorView的draw(Canvas canvas)方法的流程:

/**1*/ ApplicationThread的onTransact方法接收到SystemServer進程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION啟動Activity的Binder信息
                ↓
/**2*/ ApplicationThread.scheduleLaunchActivity() //
                ↓
/**3*/ ActivityThread.scheduleLaunchActivity() //安排啟動Activity
                ↓
/**4*/ ActivityThread.handleLaunchActivity()  //處理啟動Activity
                ↓
/**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume會使DecorView跟ViewRootImpl關聯
                ↓
/**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                ↓
/**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl關聯并繪制界面
                ↓
/**8*/ ViewRootImpl.requestLayout() //請求繪制ViewTree
                ↓
/**9*/ ViewRootImpl.scheduleTraversals() // 安排遍歷 
                ↓
/**10*/ ViewRootImpl.doTraversal() //
                ↓
/**11*/ ViewRootImpl.performTraversals() //執行遍歷 會根據情況調用relayoutWindow performMeasure performLayout performDraw 等方法 這四個方法跟繪制是緊密相關的
                ↓
/**12*/ ViewRootImpl.performDraw() //執行繪制
                ↓
/**13*/ ViewRootImpl.draw(boolean fullRedrawNeeded)//區分是否支持硬件加速來走不同的繪制流程                
                ↓
             .........
                ↓
/**14*/ DecorView.draw(Canvas canvas)  //不管走不走硬件加速都會調到這里

整個界面的繪制從 DecorView.draw(Canvas canvas)方法開始一觸即發!

一、draw(canvas,parent,drawingTime)和draw(canvas)作用不同

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
  /**
     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
     * 此方法是被父控件ViewGroup.drawChild()調用的
     * drawChild()方法又是被ViewGroup中的dispatchDraw(Canvas canvas)方法調用的
     * This is where the View specializes rendering behavior based on layer type,
     * and hardware acceleration.
     */
    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
        /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
         *
         * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
         * HW accelerated, it can't handle drawing RenderNodes.
         */
         //判斷當前View是否支持硬件加速繪制
        boolean drawingWithRenderNode = mAttachInfo != null
                && mAttachInfo.mHardwareAccelerated
                && hardwareAcceleratedCanvas;

        ......略

        //硬件加速繪制用到的繪制節點
        RenderNode renderNode = null;
        //cpu繪制用到的繪制緩存
        Bitmap cache = null;
        //獲取當前View的繪制類型 LAYER_TYPE_NONE LAYER_TYPE_SOFTWARE LAYER_TYPE_HARDWARE
        int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
        if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {  //如果是cpu繪制類型
             if (layerType != LAYER_TYPE_NONE) {
                 // If not drawing with RenderNode, treat HW layers as SW
                 layerType = LAYER_TYPE_SOFTWARE;
                 //開始cpu繪制緩存構建
                 buildDrawingCache(true);
            }
            //獲得cpu繪制緩存結果 存儲在Bitmap中
            cache = getDrawingCache(true);
        }

        if (drawingWithRenderNode) { //支持硬件加速
            // Delay getting the display list until animation-driven alpha values are
            // set up and possibly passed on to the view
            //更新gpu繪制列表 保存在RenderNode中
            renderNode = updateDisplayListIfDirty();
            if (!renderNode.isValid()) {
                // Uncommon, but possible. If a view is removed from the hierarchy during the call
                // to getDisplayList(), the display list will be marked invalid and we should not
                // try to use it again.
                renderNode = null;
                //gpu繪制失敗標識
                drawingWithRenderNode = false;
            }
        }

        ......略

        //cpu繪制成功并且gpu繪制失敗了
        final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;

        ......略


        if (!drawingWithDrawingCache) { //走gpu繪制
            if (drawingWithRenderNode) { //支持gpu繪制
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                //gpu繪制收集到的DisplayList
                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
            } else { //走gpu繪制又突然不支持gpu繪制(可能是極限情況下)
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    //沒有內容不需要繪制自己,就直接向下分發繪制子View
                    dispatchDraw(canvas);
                } else {
                    //繪制自己后再分發繪制子View
                    draw(canvas);
                }
            }
        } else if (cache != null) { //走cpu繪制且cpu繪制緩存不為null

         ......略

            //把存儲cpu繪制緩存的Bitmap用canvas走cpu繪制(skia渲染引擎)
            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
          
        }
         ......略

        return more;
    }

    /**
     * Manually render this view (and all of its children) to the given Canvas.
     * The view must have already done a full layout before this function is
     * called.  When implementing a view, implement
     * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
     * If you do need to override this method, call the superclass version.
     *
     * @param canvas The Canvas to which the View is rendered.
     */
    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        int saveCount;
        // Step 1 繪制背景
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        //Step 2,必要時,保存畫布的圖層為褪色做準備
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, 繪制View自身內容
            if (!dirtyOpaque) onDraw(canvas);
  
            //Step 4, 繪制子View的內容
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            //Step 5, 必要時,繪制褪色邊緣并恢復圖層,通過        getOverlay().add(drawable); 添加圖片什么的
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, 繪制裝飾(列如滾動條)
            onDrawForeground(canvas);

            // we're done...
            return;
        }

        ......略
}

可以看到View中的 updateDisplayListIfDirty()方法是gpu繪制的關鍵,buildDrawingCache()方法是cpu繪制的關鍵。
updateDisplayListIfDirty()buildDrawingCache()方法都會調用到View的draw(canvas)方法,但是updateDisplayListIfDirty()方法中給draw(canvas)傳的是DisplayListCanvas參數,使其具備HWUI的功能。buildDrawingCache()方法中給draw(canvas)方法傳入的是普通的Canvas。

也可以很清楚滴看到,對于我們開發者來說,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法就是一個View的繪制入口,從這個方法中決定了走cpu繪制還是gpu繪制。

draw(Canvas canvas)方法是具體的繪制工作,如果是gpu硬件加速繪制,則使用DisplayListCanvas畫布繪制,會把繪制DisplayList保存在繪制節點RenderNode中。如果是CPU軟繪制,則使用普通的Canvas畫布繪制,把繪制緩存保存在一個Bitmap中,最后會使用canvas.drawBitmap()方法使用skia渲染引擎cpu繪制緩存Bitmap中的數據。

二、頂級DecorView硬件加速調用draw(canvas)

注意:
DecorView其實是一個FrameLayout,FrameLayout是一個ViewGroup,ViewGroup是一個繼承View的抽象類,draw(canvas)方法只在View類中有實現,所以調用DecorViewdraw(canvas)其實最終調用的是Viewdraw(canvas)方法。

上面已經說了,DecorView是頂級View,它的draw(canvas)方法是繪制的開端,那么在硬件加速下ViewRootImpl是怎么調用到DecorView的draw(canvas)的呢?

得從ViewRootImpl的draw(boolean fullRedrawNeeded)方法開始分析:


/***********************************************************************        
   /**1*/ ApplicationThread的onTransact方法接收到SystemServer進程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION啟動Activity的Binder信息
                ↓
   /**2*/ ApplicationThread.scheduleLaunchActivity() //
                   ↓
   /**3*/ ActivityThread.scheduleLaunchActivity() //安排啟動Activity
                   ↓
   /**4*/ ActivityThread.handleLaunchActivity()  //處理啟動Activity
                   ↓
   /**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume會使DecorView跟ViewRootImpl關聯
                   ↓
   /**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                   ↓
   /**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl關聯并繪制界面
                   ↓
   /**8*/ ViewRootImpl.requestLayout() //請求繪制ViewTree
                   ↓
   /**9*/ ViewRootImpl.scheduleTraversals() // 安排遍歷 
                   ↓
   /**10*/ ViewRootImpl.doTraversal() //
                   ↓
   /**11*/ ViewRootImpl.performTraversals() //執行遍歷 會根據情況調用relayoutWindow performMeasure performLayout performDraw 等方法 這四個方法跟繪制是緊密相關的
                   ↓
   /**12*/ ViewRootImpl.performDraw() //執行繪制
                   ↓
   /**13*/ 區分是否支持硬件加速來走不同的繪制流程*********************************/  

   private void draw(boolean fullRedrawNeeded) {
      
      ......略

        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { //支持硬件加速并且需要繪制
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
            
                ......略
                //1 硬件加速調DecorView 的draw(canvas)方法的關鍵
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else {
            
                ......略
                
                //2 非硬件加速調DecorView的draw(canvas)方法的關鍵
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }
               ......略
    }
}

ThreadedRenderer是5.0上為每個進程新增了一個RenderThread線程,既一個渲染線程,RenderThread線程可以保證在主線程阻塞的情況下動畫執行依然流暢順滑。就是一個異步繪制的處理線程。
更多請參看:
http://www.lxweimin.com/p/bc1c1d2fadd1
http://blog.csdn.net/guoqifa29/article/details/45131099

我們先分析硬件加速調用DecorViewdraw(canvas)方法,先看mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this)里面的流程:

/**
 *5.0新增的渲染線程
 */
public final class ThreadedRenderer {
    ......略 
    /**
     * Draws the specified view.
     *
     * @param view The view to draw.
     * @param attachInfo AttachInfo tied to the specified view.
     * @param callbacks Callbacks invoked when drawing happens.
     */
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        
        //1 圈起來 終點 要考的 ,其實就是更新`DecorView`的`DisplayList`
        updateRootDisplayList(view, callbacks);
      
    }
    
    //其實就是更新`DecorView`的`DisplayList`
    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {

        //更新`DecorView`的`DisplayList`
        updateViewTreeDisplayList(view);

        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
            //1 獲取一個DisplayListCanvas畫布
            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
            try {
                final int saveCount = canvas.save();
                canvas.translate(mInsetLeft, mInsetTop);
                callbacks.onHardwarePreDraw(canvas);

                canvas.insertReorderBarrier();
                
                //2 繪制獲取到的DecorView的RenderNode
                //view.updateDisplayListIfDirty()其實是調用的DecorView的updateDisplayListIfDirty方法,
                //通過層層調用updateDisplayListIfDirty方法最終會獲取整個view tree的繪制節點`RenderNode`

                canvas.drawRenderNode(view.updateDisplayListIfDirty());
                
                canvas.insertInorderBarrier();

                callbacks.onHardwarePostDraw(canvas);
                canvas.restoreToCount(saveCount);
                mRootNodeNeedsUpdate = false;
            } finally {
                //3 整個View tree繪制結束后回收資源
                mRootNode.end(canvas);
            }
        }
    }
  ......略
}

從上面可以看到 更新DecorViewDisplayList而調用 updateViewTreeDisplayList(view)方法,這個方法請看:

/**
 *5.0新增的渲染線程
 */
public final class ThreadedRenderer {
    ......略
    private void updateViewTreeDisplayList(View view) {
        view.mPrivateFlags |= View.PFLAG_DRAWN;
        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                == View.PFLAG_INVALIDATED;
        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    //其實也是更新DecorView的DisplayList而調用view.updateDisplayListIfDirty()方法
        view.updateDisplayListIfDirty();
        view.mRecreateDisplayList = false;
    }
    ......略
}

看到這里那么可以知道,硬件加速的情況下,DecorViewupdateDisplayListIfDirty方法是關鍵,也是從這里調用到DecorView的的draw(canvas)方法開啟繪制的,請看源代碼:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
        
   ......略
   
    /**
     * 更新一個View的繪制DisplayList保存在RenderNode返回 
     * 返回的RenderNode是被ThreadedRenderer線程的drawRenderNode(RenderNode)方法繪制的
     * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
     * @hide
     */
    @NonNull
    public RenderNode updateDisplayListIfDirty() {
        final RenderNode renderNode = mRenderNode;
        
               ......略
            
            //從renderNode中獲取一個DisplayListCanvas
            final DisplayListCanvas canvas = renderNode.start(width, height);
            canvas.setHighContrastText(mAttachInfo.mHighContrastText);

            try {
                if (layerType == LAYER_TYPE_SOFTWARE) { //為CPU繪制draw(canvas)方法
                    buildDrawingCache(true); //創建CPU繪制緩存會調用到View的
                    Bitmap cache = getDrawingCache(true);  //保存CPU繪制緩存
                    if (cache != null) {
                        canvas.drawBitmap(cache, 0, 0, mLayerPaint); //skia繪制收集到的Bitmap緩存數據
                    }
                } else { //為硬件加速GPU繪制
                    computeScroll();

                    canvas.translate(-mScrollX, -mScrollY);
                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;

                    // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        dispatchDraw(canvas); //View本身不需要繪制 直接分發給子View繪制
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.getOverlayView().draw(canvas);
                        }
                    } else {
                        //使用DisplayListCanvas繪制,需要的繪制會保存在DisplayList
                        draw(canvas);
                    }
                }
            } finally {
                renderNode.end(canvas);  //回收資源
                setDisplayListProperties(renderNode);
            }
        } else {
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        }
        return renderNode; //返回保存有GPU繪制數據DisplayList的繪制節點renderNode
    }
    
   ......略
   
}

可以看到在View的updateDisplayListIfDirty方法中,支持硬件加速的情況下準備好RenderNodeDisplayListCanvas后直接調用了View的draw(canvas)方法。

總結流程:

//前提是需要支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded) 
                   ↓
ThreadedRenderer.draw(view,attachInfo,hardwareDrawCallbacks)                   
                   ↓           
ThreadedRenderer.updateRootDisplayList(view, callbacks)
                   ↓
DecorView.updateDisplayListIfDirty()
                   ↓  
        DecorView.draw(canvas)

三、頂級DecorView非硬件加速調用draw(canvas)

從上面的ViewRootImpldraw(boolean fullRedrawNeeded)方法中可以看到,如果是CPU繪制,就會走drawSoftware()方法。那么我們看一下drawSoftware()中是怎么調到DecorView的draw(canvas)方法的:

ViewRootImpl``drawSoftware()方法:

    /**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        
            ......略
            
            //從Surface中獲取一個普通的Canvas
            canvas = mSurface.lockCanvas(dirty);

            ......略

            //調用DecorView的draw(canvas)方法
            mView.draw(canvas);

            ......略

        return true;
    }

總結流程:

//前提是不支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded) 
                   ↓
ViewRootImpl.drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)                  
                   ↓  
        DecorView.draw(canvas)

DecorView的draw(canvas)方法調用總結:

DecorView作為頂級View的存在,它的繪制是由ViewRootImpl判斷是CPU還是GPU繪制然后調用DecorViewdraw(canvas)方法,開啟整個界面的繪制。

其余的View,都有是自己的父控件調用 draw(canvas,parent,drawingTime)方法,在draw(canvas,parent,drawingTime)方法中判斷當前View是CPU還是GPU繪制然后調用draw(canvas)

四、非頂級View硬件加速draw(canvas, parent, drawingTime)調用draw(Canvas canvas)

上面我已經說了,整個界面的繪制是從DecorViewdraw(canvas)方法開始,普通View的繪制是從draw(canvas, parent, drawingTime)開始。
View的draw(canvas, parent, drawingTime)、draw(Canvas canvas)和updateDisplayListIfDirty()三個方法我這里就不粘貼了,上已經有了,直接給出流程吧:

//GPU繪制
Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime)
     ↓
Vew.updateDisplayListIfDirty()
     ↓
Vew.draw(displayListCanvas)

五、非頂級View非硬件加速draw(canvas, parent, drawingTime)調用draw(Canvas canvas)

先給出CPU繪制流程:

//CPU繪制
Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime)
     ↓
Vew.buildDrawingCache(boolean autoScale)
     ↓
Vew.buildDrawingCacheImpl(boolean autoScale)
     ↓ 
Vew.draw(displayListCanvas)

draw(canvas, parent, drawingTime)方法調用到buildDrawingCache在上面的代碼中可以看到,這里就看一下buildDrawingCachebuildDrawingCacheImpl方法:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
        
   ......略
   
       public void buildDrawingCache(boolean autoScale) {
        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                mDrawingCache == null : mUnscaledDrawingCache == null)) {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                        "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
            }
            try {
                //執行CPU繪制緩存創建
                buildDrawingCacheImpl(autoScale);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    }
    
       ......略
       
    /**
     * private, internal implementation of buildDrawingCache, used to enable tracing
     */
    private void buildDrawingCacheImpl(boolean autoScale) {
    
           ......略
            
            //保存CPU繪制緩存的Bitmap
            Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
            
           ......略
        if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
        
            ......略
                   
            try {
                //如果緩存Bitmap為空就重新創建和賦值
                bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                        width, height, quality);
                bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
                if (autoScale) {
                    mDrawingCache = bitmap;
                } else {
                    mUnscaledDrawingCache = bitmap;
                }
                if (opaque && use32BitCache) bitmap.setHasAlpha(false);
            } catch (OutOfMemoryError e) {
            
                ......略
            }

            clear = drawingCacheBackgroundColor != 0;
        }

        Canvas canvas;
        if (attachInfo != null) { //處理Canvas
            canvas = attachInfo.mCanvas;
            if (canvas == null) {
                canvas = new Canvas();
            }
            //canvas的Bitmap設置為我們創建的緩存Bitmap
            canvas.setBitmap(bitmap);
            
            ......略
            
        } else {
            // This case should hopefully never or seldom happen
            canvas = new Canvas(bitmap);
        }
            ......略

        // Fast path for layouts with no backgrounds
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            
            dispatchDraw(canvas); //自己不需要繪制,直接分發子View繪制
            
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().draw(canvas);
            }
        } else {
            //自己需要繪制,然后再分發子View繪制,所有的繪制都會畫在緩存Bitmap上面
            draw(canvas);
        }

            ......略
    }
   
   ......略
   
   }

繪制總結:

不管是支持還是不支持硬件加速,都會調用到View的draw(canvas)方法。
只是硬件加速的情況下為DisplayListCanvas畫布,得到的DisplayList數據保存在每一個View的繪制節點RenderNode中,最后交給DisplayListCanvasdrawRenderNode(renderNode)方法處理渲染操作。
非硬件加速的情況下,會把所有的繪制緩存數據保存到一個緩存Bitmap中,然后由Canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint)負責把數據交給skia渲染。

DisplayList構建分析請看:http://www.lxweimin.com/p/7bf306c09c7e

整體測量定位繪制流程總結總結:

其實draw流程相對于MeasureLayout來說特殊一些,為什么這么說?如果你了解view的整個流程就知道,MeasureLayout流程在ViewViewGroup這兩個基本控件中都沒有具體實現,而View的繪制在View這個基本的類中都實現了,而且在View的raw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中區分了cpugpu繪制來走不同的流程。View的draw(Canvas canvas)方法實現了具體的6個繪制步驟,ViewGroup中的dispatchDraw(Canvas canvas)方法實現了具體的子View的繪制分發。

為什么是這樣?

因為為基本的ViewViewGroup控件不能決定具體的樣子,這里說的樣子更多偏重于控件的排列,大小寬高,之間的相對位置,這些屬性都是由,MeasureLayout流程決定的。所以不管你怎么走MeasureLayout流程,其draw繪制都是一樣的。draw繪制出來的樣子是由具體的LinearLayoutFrameLayoutRelativeLayoutRecyclerView等等控件的MeasureLayout流程決定的。所以MeasureLayout流程需要在具體的控件中具體實現。

當然上面說的是系統的控件,LinearLayoutFrameLayout這些,自定義的View或者ViewGroup的流程那就完全由開發者自己說了。

不清楚繪制流程的請看:http://blog.csdn.net/yanbober/article/details/46128379

硬件繪制軟件繪不足之處

目前硬件繪制軟件繪制都存在不足,比如(來自:http://blog.csdn.net/jxt1234and2010/article/details/47326411):
臟區域識別之后并沒有充分地優化 。

軟件渲染時,盡管限制了渲染區域,但所有ViewonDraw方法一個不丟的執行了一遍。 硬件渲染時,避免了沒刷新的View調onDraw方法更新顯示列表,但顯示列表中的命令仍然一個不落的在全屏幕上執行了一遍。

一個比較容易想到的優化方案就是為主流程中的View建立一個R-Tree索引,invalidate這一接口修改為可以傳入一個矩形范圍R,更新時,利用R-Tree索引找出包含R的所有葉子View,令這些View在R范圍重繪一次即可。

這個槽點其實影響倒不是很大,大部分情況下View不多,且如果出現性能問題,基本上都是一半以上的屏幕刷新。

對不住大家了,有一個硬件繪制很關鍵大方法dispatchGetDisplayList()沒講到,抱歉了。罪過罪過啊!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容