【Androidソース】Viewの描画フロー分析

10971 ワード

Activityのresume


Activityの起動プロセス
前回Activityの起動プロセスでは、ActivityのライフサイクルがHandlerによってメッセージを送信することによって実行されることがわかりましたが、Viewの描画はonResumeの後です.
// H();
 case RESUME_ACTIVITY:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
     SomeArgs args = (SomeArgs) msg.obj;
     handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
             args.argi3, "RESUME_ACTIVITY");
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
     
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    //  onResume 
    r = performResumeActivity(token, clearHide, reason);
    //  decorView 
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
    wm.addView(decor, l);
}

// WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
   applyDefaultToken(params);
   mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
  • handleResumeActivityメソッドは、まず、performResumeActivity、すなわち、我々のActivityにおけるonResumeメソッドを呼び出す.
  • decorViewを取得することによって、私たちのレイアウトは実際にdecorViewにロードされ、wmのaddViewメソッドによってdecorViewが伝達されます.
  • wmは実際にはWindowManagerImplであり、WindowManagerImplではWindowManagerGlobaladdViewメソッドが呼び出される.
  • // WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
        ViewRootImpl root;
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        
        root.setView(view, wparams, panelParentView);
    }  
    
    // ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        requestLayout();
    }
    
    @Override
    public void requestLayout() {
       if (!mHandlingLayoutInLayoutRequest) {
           checkThread();
           mLayoutRequested = true;
           scheduleTraversals();
       }
    }
    
    void scheduleTraversals() {
           mChoreographer.postCallback(
                   Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    final class TraversalRunnable implements Runnable {
      @Override
      public void run() {
          doTraversal();
      }
    }
    
    

    addViewメソッドではまずViewRootImplという非常に重要なクラスが構築され,このクラスはViewの描画フローを操作するクラスであり,setViewメソッドでViewを設定する.setViewメソッドでは、ViewGroupをカスタマイズするときにレイアウトを更新すると、このメソッドを呼び出してインタフェースをリフレッシュします.実は、Viewの再描画プロセスを呼び出します.一連の呼び出しの後、requestLayoutメソッドが最終的に呼び出された.

    measure

    void doTraversal() {
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
       Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
       try {
           mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
       } finally {
           Trace.traceEnd(Trace.TRACE_TAG_VIEW);
       }
    }
    
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
    doTraversalではdoTraversalが呼び出され、performMeasureではViewのonMeasureメソッドが呼び出され、これが本格的な測定を開始します.
    LinearLayoutのperformMeasureで分析します.
    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                 measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                            heightMeasureSpec, usedHeight);
             }
                if (useLargestChild &&
               (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
           mTotalLength = 0;
    
           for (int i = 0; i < count; ++i) {
               final View child = getVirtualChildAt(i);
               if (child == null) {
                   mTotalLength += measureNullChild(i);
                   continue;
               }
    
               if (child.getVisibility() == GONE) {
                   i += getChildrenSkipCount(child, i);
                   continue;
               }
    
               final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                       child.getLayoutParams();
               // Account for negative margins
               final int totalLength = mTotalLength;
               mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                       lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
           }
       }
    
       // Add in our padding
       mTotalLength += mPaddingTop + mPaddingBottom;
    
       int heightSize = mTotalLength;
    
           int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
           setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                    heightSizeAndState);
    }
    
    void measureChildBeforeLayout(View child, int childIndex,
           int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
           int totalHeight) {
       measureChildWithMargins(child, widthMeasureSpec, totalWidth,
               heightMeasureSpec, totalHeight);
    }
    
    protected void measureChildWithMargins(View child,
           int parentWidthMeasureSpec, int widthUsed,
           int parentHeightMeasureSpec, int heightUsed) {
       final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
       final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
               mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                       + widthUsed, lp.width);
       final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
               mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                       + heightUsed, lp.height);
    
       child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    

    まずすべてのサブビューを巡回し、次にサブビューのmeasure法により測定を継続し、サブビューがビューグループであれば巡回を継続し、ビューであればサブビューの幅を直接測定することができる.一方、ViewのmeasureではonMeasureメソッドが呼び出されています.このメソッドは、私たちが普段書いているカスタムViewを上書きする方法で、私たちがカスタムViewの幅を指定します.
    最下層のViewから測定を開始し,最下層のView Groupまで最終的に測定した.最外層を測定すると、再び自分のサブViewを巡ってサブViewの幅を計算します.最終呼び出しsetMeasuredDimensionは、幅と高さを設定します.

    layout

    void doTraversal() {
        performLayout(lp, mWidth, mHeight);
    }
    
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
           int desiredWindowHeight) {
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }
    
    public void layout(int l, int t, int r, int b) {
        onLayout(changed, l, t, r, b);
    }
    

    performMeasureメソッドの実行が完了すると、VERTICALはまた、doTraversalを呼び出して、サブViewの位置を決定する.performLayoutはViewのlayoutメソッドを呼び出し続け、ここではよく知られているperformLayoutにも会いました.
    同じくLinearLayoutのonLayoutで分析した.
    void layoutVertical(int left, int top, int right, int bottom) {
        for (int i = 0; i < count; i++) {
               setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                       childWidth, childHeight);
               childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
    
               i += getChildrenSkipCount(child, i);
           }
       }
    }
    
    private void setChildFrame(View child, int left, int top, int width, int height) {        
       child.layout(left, top, left + width, top + height);
    }
    

    layoutVerticalは、すべてのサブViewを巡回し、setChildFrameを呼び出してサブViewに対応する場所を指定します.このときlayoutとmeasureの計算方式はまた異なり,measureは深さ優先遍歴方式で,まずサブViewの幅を取得し,layoutがViewの位置を指定する場合は,外向内から順に指定Viewの位置を遍歴する.

    draw

    void doTraversal() {
        performDraw();
    }
    
    private void performDraw() {
       try {
             //  
           draw(fullRedrawNeeded);
       } finally {
           mIsDrawing = false;
           Trace.traceEnd(Trace.TRACE_TAG_VIEW);
       }
    }
    
    private void draw(boolean fullRedrawNeeded) {
        //  surface
       Surface surface = mSurface;
       if (!surface.isValid()) {
           return;
       }
        //  
       if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
               //  
           if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                    //  
               mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
           } else {
                   //  cpu 
               if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                   return;
               }
           }
       }
    
       if (animating) {
           mFullRedrawNeeded = true;
           scheduleTraversals();
       }
    }
    

    performMeasureメソッドが実行されると、VERTICALはまたdoTraversalを呼び出してビューの描画を開始する.draw関数で描画する領域を取得し、ハードウェアアクセラレータを使用するかどうかを判断します.通常はcpuを使用して描画されます.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;
    
                //  canvas , framework 
           canvas = mSurface.lockCanvas(dirty);
       } catch (Surface.OutOfResourcesException e) {
               return false;
       }
    
       try {
               //  , decorView 
             mView.draw(canvas);
       } finally {
               //  canvas , SurfaceFlinger 
               // private static native void nHwuiDraw(long renderer);  native 
               surface.unlockCanvasAndPost(canvas);
       }
       return true;
    }
    

    上の関数から描画の手順がわかります.
  • gpuを使用するかcpuを使用するかを判断する
  • ペイントされたsurfaceオブジェクト
  • を取得する
  • surfaceによりcanvasオブジェクト
  • を取得しロックする
  • DecorViewからビューツリー全体の描画を開始する
  • canvasオブジェクトをロック解除し、surfaceFlingerオブジェクトにビューの更新を通知し、nativeメソッドを呼び出す.

  • Viewのdraw関数を見てみましょう.
    public void draw(Canvas canvas) {
        drawBackground(canvas);
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);
            
            // Step 4, draw the children
            dispatchDraw(canvas);
            
            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
             mOverlay.getOverlayView().dispatchDraw(canvas);
            }
            
            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);
            
            // we're done...
            return;
        }
    }
    

    ViewのdrawメソッドはperformDrawを呼び出してペイントプロセスを渡し、dispatchDrawはすべてのサブ要素のdrawメソッドを呼び出し、drawは最後のViewペイントが完了したことを知るようにします.
    この時点でViewの描画プロセス全体が完了します.