Android显示系统探索二:Window创建、测量与显示


前言

  我们将Andoird显示系统 探索一的Java代码修改成如下:

WindowManager mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams lps = new WindowManager.LayoutParams();
//设置层级
lps.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
lps.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
//设置窗口位置
lps.width = 500;
lps.height = 500;
lps.gravity = Gravity.LEFT | Gravity.TOP;
lps.x = 100;
lps.y = 100;
lps.token = new Binder();
//设置窗口动画
lps.windowAnimations = R.style.Animation_AppCompat_DropDownUp;
//纯色背景
View view = LayoutInflater.from(context).inflate(R.layout.window_overlay, null);
mWindowManager.addView(view, lps);

本章我们从下面四个方面简述android从addView开始的流程:

  1. Window创建
  2. Window窗口测量
  3. Window层级运算
  4. 窗口动画

android环境为android 11(API 30)。


一、Window创建

  mWindowManager.addView方法在进程内调用流程如下图所示,在ViewRootImpl内部setView()方法通过AIDL调用Session内部方法addToDisplayAsUser进入SystemServer进程。Window创建过程在之前的文章InputManagerService源码分析三中有提及。
addView流程进入SystemSever进程

图1: addView时序图
ViewRootImpl部分代码如下所示:
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...
                try {
                    ...
                    //调用WindowManagerService
                    res = mWindowSession.addToDisplayAsUser(...);
                    //更新应用层View位置信息,不一定准确,后续relayout方法会更新mTmpFrame
                    setFrame(mTmpFrame);
                } catch (RemoteException e) {
                    ....
                }
            }
        }

1.1 创建WindowToken、WindowState

  addToDisplayAsUser方法字明其意,就是将Window添加到屏幕上。android系统支持多屏幕显示,android系统中屏幕对应有一个实例DisplayContent。addToDisplayAsUser方法后续会调用WindowManagerService内的addWindow()方法,创建的WindowToken和WindowState,并添加到对应的DisplayContent中。addWindow部分代码如下所示:

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
      ...
      //窗口有效性监测
      int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
                appOp);
      ...
      synchronized (mGlobalLock) {
      ...
      //找到Window需要attch到的屏幕DisplayContent
      final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
      ...
      //示例代码type为TYPE_APPLICATION_OVERLAY,activity为null
      ActivityRecord activity = null;
      final boolean hasParent = parentWindow != null;
      //获取WindowToken;如果通过Activity启动的返回的是ActivityRecord。示例代码,返回为null
      WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
      }
      if (token == null) {
        ...
        //type为TYPE_APPLICATION_OVERLAY启动的悬浮窗是没有父窗口
        if (hasParent) {
            //子窗口跟父窗口使用同一个token
            token = parentWindow.mToken;
        } else {
            //流程进入到创建WindowToken流程
            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
        }
     } else{
         ...
     }
     //创建WindowState
     final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
     //建立Input通道,客户端可以接受Input事件
	final boolean openInputChannels = (outInputChannel != null
	                 && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
	if (openInputChannels) {
	    win.openInputChannel(outInputChannel);
	}
	...
	//设置WindowToken的子节点WindowState
	win.mToken.addWindow(win);
	...
}
...

1.2 更新WindowToken父节点

  WindowToken构造函数如下所示,构造函数内部调用DisplayContent的addWindowToken的方法将WindowToken添加到DisplayContent内部。

//frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
            boolean roundedCornerOverlay, boolean fromClientToken) {
            super(service);
        token = _token;
        windowType = type;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mOwnerUid = ownerUid;
        mRoundedCornerOverlay = roundedCornerOverlay;
        mFromClientToken = fromClientToken;
        //将Token添加到DisplayContent中
        if (dc != null) {
            dc.addWindowToken(token, this);
        }
        ...
}

  addWindowToken方法如下所示。应用层设置的type为TYPE_APPLICATION_OVERLAY,流程会进入mDisplayAreaPolicy.addWindow(),将创建的WindowToken添加到Window树中。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void addWindowToken(IBinder binder, WindowToken token) {
        final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
        mTokenMap.put(binder, token);
        //应用程序代码type为TYPE_APPLICATION_OVERLAY,token为WindowToken,进入流程
        if (token.asActivityRecord() == null) {
            token.mDisplayContent = this;
            switch (token.windowType) {
                case TYPE_INPUT_METHOD:
                case TYPE_INPUT_METHOD_DIALOG:
                    mImeWindowsContainers.addChild(token);
                    break;
                case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
                    // TODO(display-area): Migrate to DisplayArea
                    mOverlayContainers.addChild(token);
                    break;
                default:
                    //type为TYPE_APPLICATION_OVERLAY,示例代码进入到该流程
                    mDisplayAreaPolicy.addWindow(token);
                    break;
            }
        }
    }

  Android 11中抽象类DisplayAreaPolicy唯一继承类为Result,mDisplayAreaPolicy.addWindow()继而调用Result中的addWindow方法。Result类的部分代码如下所示:

//frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
static class Result extends DisplayAreaPolicy {
    ...
    @Override
    public void addWindow(WindowToken token) {
        //步骤1.获取Token需要添加的父节点
        DisplayArea.Tokens area = findAreaForToken(token);
        //步骤2.将token作为子节点添加到父节点
        area.addChild(token);
    }

    DisplayArea.Tokens findAreaForToken(WindowToken token) {
        int windowLayerFromType = token.getWindowLayerFromType();
        if (windowLayerFromType == APPLICATION_LAYER) {
            windowLayerFromType += 1;
        } else if (token.mRoundedCornerOverlay) {
            windowLayerFromType = mMaxWindowLayer - 1;
        }
        //mAreaForLayer数组长度为35,根据type选择DisplayArea.Tokens
        return mAreaForLayer[windowLayerFromType];
    }
    ...
}

从代码中看出addWindow主要分两步:
步骤 1: 获取Token需要添加的父节点Tokens
  通过token的type属性获取对应的DisplayArea.Tokens,在Android 11中mAreaForLayer的长度为35,但是它只有四种成员。

  1. DisplayArea$Tokens “Leaf:0:1”
  2. DisplayArea$Tokens “Leaf:2:2”
  3. DisplayArea$Tokens “Leaf:3:14”
  4. DisplayArea$Tokens “Leaf:17:34”

后面字符串参数的格式:

Leaf:minLayer:maxLayer

其中minLayer最小层级,maxLayer最大层级。Android 11中TYPE_APPLICATION_OVERLAY对应的层级为12,所以返回的为Leaf:3:14。
步骤 2: 将token作为子节点添加到Tokens中
  这个过程1.2.2节介绍,下一节先介绍WMS的Window容器的继承关系。

1.2.1 Window容器简介

  WindowManagerService架构设计特色之一就是它各种容器类之间的继承关系,下图展示的就是WindowManagerService各种容器类的继承关系。
在这里插入图片描述

图2: Window容器类之间的继承关系

Window容器继承关系、父节点、子节点的类型非常重要,理解了他们才能理解才能更清晰的理解WindowManagerService架构。 下面罗列的是Window容器的子节点类型。

  1. RootWindowContainer的mChildren参数类型为DisplayContent;
  2. DisplayContent的mChildren的成员为WindowContainers,NonAppWindowContainers
  3. WindowContainers的mChildren的成员为Root;
  4. Root的mChildren参数类型为DisplayArea,参数又可以细分为Tokens、ImeContainer、TaskDisplayArea;
  5. Tokens的mChildren参数类型为WindowToken;
  6. TaskDisplayArea的mChildren参数类型为ActivityStack;
  7. WindowToken的mChildren参数类型为WindowState;
  8. WindowState的mChildren参数类型为WindowState;

各个子容器之间的父子关系如下:

Window容器类之间的父子关系

图3: Window容器类之间的父子关系

这里把Root的成员变量mChildren单独罗列出来(android 11),如下所示:

  1. DisplayArea$Tokens “Leaf:0:1”
  2. DisplayArea$Tokens “Leaf:2:2”
  3. TaskDisplayArea “DefaultTaskDisplayArea”
  4. DisplayArea$Tokens “Leaf:3:14”
  5. DisplayContent$ImeContainer “ImeContainer”
  6. DisplayArea$Token “Leaf:17:34”

之所以把他罗列出来是因为在后续的Window层级设置中起到至关重要的作用。

1.2.2 设置WindowToken父节点

1.2节中我们简述了步骤2的作用。本节主要addChild这个过程,部分代码如下所示。将创建的WindowToken添加到Tokens内部使用了一个比较器Comparator,比较器的规则:

  WindowToken::getWindowLayerFromType

最终调用WindowManagerPolicy内的getWindowLayerFromTypeLw方法,通过type获取对应的Window层级。

//frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java
public static class Tokens extends DisplayArea<WindowToken> {

private final Comparator<WindowToken> mWindowComparator =
                Comparator.comparingInt(WindowToken::getWindowLayerFromType);
  ...
  void addChild(WindowToken token) {
      addChild(token, mWindowComparator);
  }
  ...
}

1.2.1节 我们介绍了Window容器的继承关系,Tokens内部调用父类WindowContainer.java的addChild方法。本小节从4个步骤介绍addChild这个过程。

//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
@CallSuper
protected void addChild(E child, Comparator<E> comparator) {
    ...
    int positionToAdd = -1;
    //步骤1:child添加到指定位置
    if (comparator != null) {
        final int count = mChildren.size();
        for (int i = 0; i < count; i++) {
            //根据层级比较获取child需要插入的位置
            if (comparator.compare(child, mChildren.get(i)) < 0) {
                positionToAdd = i;
                break;
            }
        }
    }
    if (positionToAdd == -1) {
        mChildren.add(child);
    } else {
    //将child添加到指定位置,之前位置数据往后平移
        mChildren.add(positionToAdd, child);
    }
    //步骤2:设置child的父节点,这时this是指DisplayArea$Tokens,Leaf:3:14
    child.setParent(this);
}

final protected void setParent(WindowContainer<WindowContainer> parent) {
        final WindowContainer oldParent = mParent;
        mParent = parent;
        if (mParent != null) {
            mParent.onChildAdded(this);
        }
        if (!mReparenting) {
            if (mParent != null && mParent.mDisplayContent != null
                    && mDisplayContent != mParent.mDisplayContent) {
                onDisplayChanged(mParent.mDisplayContent);
            }
            onParentChanged(mParent, oldParent);
        }
    }

步骤1:   在addChild方法内使用了比较器排序,通过Window的层级大小进行排序:层级小的在前,层级大的在后
步骤2:  设置child的父节点,这时this是指DisplayArea$Tokens,Leaf:3:14;

//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent,
            PreAssignChildLayersCallback callback) {
        super.onParentChanged(newParent, oldParent);
        if (mParent == null) {
            return;
        }

        if (mSurfaceControl == null) {
            //步骤3:创建WindowToken内部的SurfaceControl实例
            createSurfaceControl(false /*force*/);
        } else {
            reparentSurfaceControl(getSyncTransaction(), mParent.mSurfaceControl);
        }

        if (callback != null) {
            callback.onPreAssignChildLayers();
        }
        //步骤4:调用父节点进行Z参数重新计算
        mParent.assignChildLayers();
        scheduleAnimation();
    }

步骤3:  创建WindowToken内部的SurfaceControl实例;
图3介绍Window容器类的继承关系,WindowToken内部创建的SurfaceControl,并未其指定了父SurfaceControl,在本文分析中parent值的是DisplayArea$Tokens "Leaf:3:14"内部的mSurfaceControl。

步骤4:  调用父节点进行层级设置。
这一节比较重要,单独拉出一小节单独介绍。

//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void createSurfaceControl(boolean force) {
    setInitialSurfaceControlProperties(makeSurface());
}

SurfaceControl.Builder makeSurface() {
    //获取父容器实例
    final WindowContainer p = getParent();
    //调用父对象创建SurfaceControl建造者
    return p.makeChildSurface(this);
}

SurfaceControl.Builder makeChildSurface(WindowContainer child) {
    //一层层调用,最终调用DisplayContent内的makeChildSurface方法
    final WindowContainer p = getParent();
    return p.makeChildSurface(child)
            .setParent(mSurfaceControl);
}

void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
    setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties").build());
    getSyncTransaction().show(mSurfaceControl);
    onSurfaceShown(getSyncTransaction());
    //更新SurfaceContorl的位置信息
    updateSurfacePosition();
}
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
        //最终调用DisplayContent内成员变量SurfaceSession
        SurfaceSession s = child != null ? child.getSession() : getSession();
        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
        if (child == null) {
            return b;
        }
       return b.setName(child.getName())
                .setParent(mSurfaceControl);
    }

1.2.3 更新DisPlayArea.Token子节点的Z信息

//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void assignChildLayers() {
     assignChildLayers(getSyncTransaction());
     scheduleAnimation();
}
void assignChildLayers(Transaction t) {
     int layer = 0;
     // We use two passes as a way to promote children which
     // need Z-boosting to the end of the list.
     for (int j = 0; j < mChildren.size(); ++j) {
         final WindowContainer wc = mChildren.get(j);
         wc.assignChildLayers(t);
         if (!wc.needsZBoost()) {
             wc.assignLayer(t, layer++);
         }
     }
     for (int j = 0; j < mChildren.size(); ++j) {
         final WindowContainer wc = mChildren.get(j);
         if (wc.needsZBoost()) {
             wc.assignLayer(t, layer++);
         }
     }
}
void assignLayer(Transaction t, int layer) {
     final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
     if (mSurfaceControl != null && changed) {
         setLayer(t, layer);
         mLastLayer = layer;
         mLastRelativeToLayer = null;
     }
}

1.3 更新WindowState父节点

节1.1 摘取WindowManagerService内addWindow方法,其中有方法

win.mToken.addWindow(win);

本节从该方法开始,简述更新WindowState父节点流程。WindowToken的addWindow方法如下:

//frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
void addWindow(final WindowState win) {
        if (win.isChildWindow()) {
            // Child windows are added to their parent windows.
            return;
        }
        if (mSurfaceControl == null) {
            createSurfaceControl(true /* force */);
        }
        if (!mChildren.contains(win)) {
            ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
            addChild(win, mWindowComparator);
            mWmService.mWindowsChanged = true;
        }
 }

流程同1.2.2 - 1.2.3

  1. 将WindowState添加到对应的WindowToken父节点中
  2. 更新WindowToken子节点信息

1.4 焦点变更

boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
            int topFocusedDisplayId) {
   //遍历获取新焦点对应的WindowState,这里不做详细介绍
   WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
   if (mCurrentFocus == newFocus) {
       return false;
   }
   //焦点变更,进入到如下流程
   boolean imWindowChanged = false;
   final WindowState imWindow = mInputMethodWindow;
   if (imWindow != null) {
       final WindowState prevTarget = mInputMethodTarget;
       //获取输入法Window的目标WindowState
       final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
       imWindowChanged = prevTarget != newTarget;

       if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
               && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
           assignWindowLayers(false /* setLayoutNeeded */);
       }
   }
   //imWindowChanged为true进入该流程
   if (imWindowChanged) {
       mWmService.mWindowsChanged = true;
       setLayoutNeeded();
       newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
   }
   //焦点变更通知WindowManagerService,通知辅助模式模式Service
   if (mCurrentFocus != newFocus) {
       mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
   }
   final WindowState oldFocus = mCurrentFocus;
   mCurrentFocus = newFocus;
   ...
   onWindowFocusChanged(oldFocus, newFocus);
   //通知DisplayPolicy,焦点变更
   int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);

   if (imWindowChanged && oldFocus != mInputMethodWindow) {
       // Focus of the input method window changed. Perform layout if needed.
       if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
           performLayout(true /*initial*/,  updateInputWindows);
           focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
       } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
           // Client will do the layout, but we need to assign layers
           // for handleNewWindowLocked() below.
           assignWindowLayers(false /* setLayoutNeeded */);
       }
   }
   //未进入该流程
   if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
       // The change in focus caused us to need to do a layout.  Okay.
       setLayoutNeeded();
       if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
           performLayout(true /*initial*/, updateInputWindows);
       } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
           mWmService.mRoot.performSurfacePlacement();
       }
   }

   if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
       // If we defer assigning layers, then the caller is responsible for doing this part.
       getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
   }
   ...
   adjustForImeIfNeeded();
   ...
   return true;
}
//frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
     mFocusedWindow = newFocus;
     mLastFocusedWindow = lastFocus;
     if (mDisplayContent.isDefaultDisplay) {
         mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
     }
     //更新状态栏
     if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
         // If the navigation bar has been hidden or shown, we need to do another
         // layout pass to update that window.
         return FINISH_LAYOUT_REDO_LAYOUT;
     }
     return 0;
}

二、Window测量

对应用层View绘制流程有一定了解的同学对类ViewRootImpl内部的方法performTraversals应该不陌生。performTraversals内部会调用performMeasure -> performLayout -> performDraw,内部分别调用View的measure -> layout -> draw方法。部分代码如下所示:

//frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
   ...
   relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
   ...
   performMeasure()
   ...
   performLayout()
   ...
   performDraw()
   ...
}
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, frameNumber,
                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize, mBlastSurfaceControl);
    ...
}

其中relayoutWindow方法通过调用WindowManagerService方法可以获取应用窗口的窗口大小,它是窗口测量、布局、绘制的基础。测量的核心代码在WindowManagerService内部,通过WindowSession代理流程进入到WindowManagerService,部分代码如下所示

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {
     ...
     //Window测量
     mWindowPlacerLocked.performSurfacePlacement(true /* force */);
     //View显示,shouldRelayout为true
     if (shouldRelayout) {
         result = win.relayoutVisibleWindow(result, attrChanges);
         try {
             //创建SurfaceControl
             result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,result, win, winAnimator);
         } catch (Exception e) {
             ...
             return 0;
         }
     }else{
     ...
     }
     ...
}

本章我们分两步介绍下WindowManagerService内部relayoutWindow方法:

  1. Window重新测量;
  2. 创建SurfaceControl

2.1 Window测量

本节以WindowPlacerLocked的 performSurfacePlacement() 方法开始Window测量,继而介绍WindowManagerService内部高频出现的方法: requestTraversal()scheduleAnimationLocked(), 最后介绍Window测量的核心方法: applySurfaceChangesTransaction()

2.1.1 Window 重新测量

  从代码层面看: performSurfacePlacementLoop() 方法最多执行6次,前提条件之一mTraversalScheduled为true,这个值在 requestTraversal() 设置。

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
        ...
        int loopCount = 6;
        do {
            mTraversalScheduled = false;
            performSurfacePlacementLoop();
            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
            loopCount--;
        } while (mTraversalScheduled && loopCount > 0);
        ...
    }

performSurfacePlacementLoop() 方法部分代码如下

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
private void performSurfacePlacementLoop() {
   ...
   mInLayout = true;
   try {
      mService.mRoot.performSurfacePlacement();
      mInLayout = false;
      //isLayoutNeeded()返回false或者达到循环上限,不继续循环
      if (mService.mRoot.isLayoutNeeded()) {
          if (++mLayoutRepeatCount < 6) {
              requestTraversal();
          } else {
              Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
              mLayoutRepeatCount = 0;
          }
      } else {
          mLayoutRepeatCount = 0;
      }
      ...
      }catch (RuntimeException e) {
         mInLayout = false;
      }
}

requestTraversal() 方法内部会调用RootWindowContainer的applySurfaceChangesTransaction(),分析Window 测量逻辑之一:分析applySurfaceChangesTransaction方法。阅读WindowManagerService源码时候,高频出现了两个方法requestTraversal()scheduleAnimationLocked(),后面分小节分析。

2.1.2 requestTraversal

requestTraversal代码如下,核心代码为设置

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
void requestTraversal() {
    if (mTraversalScheduled) {
        return;
    }
    mTraversalScheduled = true;
    ...
    mService.mAnimationHandler.post(mPerformSurfacePlacement);
}

requestTraversal流程图如下所示:
requestTraversal

requestTraversal流程到DisplayContent时序图

2.1.3 scheduleAnimationLocked

在WindowManagerService执行scheduleAnimationLocked() 方法时序图如下所示:
scheduleAnimationLocked

scheduleAnimationLocked部分时序图
这里摘取三个方法:
  1. updateWindowsForAnimator:执行窗口动画;
  2. prepareSurfaces:更新Surface位置、显示/隐藏状态;
  3. checkAppWindowReadyToShow:执行App动画。

不做赘述,感兴趣的小伙伴可以自行分析。

2.1.4 applySurfaceChangesTransaction

applySurfaceChangesTransaction() 为Window测量的核心方法。

//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacementNoTrace() {
   ...
   mWmService.openSurfaceTransaction();
   try {
       applySurfaceChangesTransaction();
   } catch (RuntimeException e) {
       Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
   } finally {
    mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
   }
}

RootWindowContainer中的 applySurfaceChangesTransaction方法,后续会调用DisplayContent中的applySurfaceChangesTransaction方法。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
  ...
  int repeats = 0;
  do {
      repeats++;
      if (repeats > 6) {
          clearLayoutNeeded();
          break;
      }
      ...
      if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
          if (updateOrientation()) {
              setLayoutNeeded();
              sendNewConfiguration();
          }
      }
      if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
          setLayoutNeeded();
      }
      // FIRST LOOP: Perform a layout, if needed.
      if (repeats < LAYOUT_REPEAT_THRESHOLD) {
          performLayout(repeats == 1, false /* updateInputWindows */);
      } else {
          Slog.w(TAG, "Layout repeat skipped after too many iterations");
      }
      //performLayout结束将pendingLayoutChanges设置为0
      pendingLayoutChanges = 0;
      Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
      try {
          mDisplayPolicy.beginPostLayoutPolicyLw();
          forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
          //返回为0跳出循环,循环次数可能小于6,pendingLayoutChanges更改地方较多,这里不做赘述
          pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
      } finally {
          Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
      }
      mInsetsStateController.onPostLayout();
  } while (pendingLayoutChanges != 0);

  Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
  try {
      forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
  } finally {
      Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
  }
}

时序图如下所示:
DisplayContent_applySurfaceChangesTransaction

applySurfaceChangesTransaction简要时序

这里简要介绍几个流程:
流程4:beginLayoutLw
  开始对Window进行测量,其中对StatusBar和NavigationBar作优先测量,可以在layoutStatusBar和layoutNavigationBar中根据需求分别定制StatusBar和Navigationbar的位置。如NavigationBar左侧布局方案探索中修改了NavigationBar的上边距和布局位置。
流程9: forAllWindows(mPerformLayout, true )
  遍历所有父Window进行测量,可以根据需求客制化父Window位置。如NavigationBar左侧布局方案探索其中对Activity、输入法窗口添加了左边距。
流程12:computeFrame
  流程9: layoutWindowLw计算了Window的各种。我们比较关心的是开头设置的Window的各种属性在framwork内部是如何运作的,下面的代码标注了应用层设置Window宽高、左右边距、Gravity属性在framework层处理,最终通过Gravity.apply方法设置Window的mFrame属性,mFrame属性后续会relayoutWindow处理结束后回传到应用端。

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
            Rect displayFrame) {
    ...
    //framework层获取应用层设置的窗口x,y值
    if (inSizeCompatMode) {
        x = mAttrs.x * mGlobalScale;
        y = mAttrs.y * mGlobalScale;
    } else {
        x = mAttrs.x;
        y = mAttrs.y;
    }
    //framework层获取根据应用层设置的窗口宽、高设置适合的宽、高
    if (inNonFullscreenContainer && !layoutInParentFrame()) {
            // Make sure window fits in containing frame since it is in a non-fullscreen task as
            // required by {@link Gravity#apply} call.
            w = Math.min(w, pw);
            h = Math.min(h, ph);
    }
   //根据应用层设置的布局位置、宽、高、x,y计算出窗口实际的位置信息,后续回传给客户段
   Gravity.apply(mAttrs.gravity, w, h, containingFrame,
           (int) (x + mAttrs.horizontalMargin * pw),
           (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame);
    ...
}

流程17: forAllWindows(mApplySurfaceChangesTransaction, true)
  遍历所有Window更新Surface,根据窗口特性可以进行:窗口平移、更新窗口动画属性、装载执行窗口动画等。下面摘取了该流程的部分代码,核心方法commitFinishDrawingLocked主要是设置窗口动画的状态、装载执行动画等。当前正在执行relayout的窗口不会执行该方法:当前窗口没有设置WindowSurfaceController对象,mHasSurface属性为false。后面会介绍Window窗口设置、动画装载的流程。下一节介绍WindowSurfaceController创建过程,会将窗口mHasSurface设置为true。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
 private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
     ...
     if (w.mHasSurface) {
            // Take care of the window being ready to display.
            final boolean committed = winAnimator.commitFinishDrawingLocked();
            if (isDefaultDisplay && committed) {
                if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                    if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                            "First draw done in potential wallpaper target " + w);
                    mWallpaperMayChange = true;
                    pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                    ...
                }
            }
        }
     ...
}

2.2 创建WindowSurfaceController

上一节介绍Window是何如测量的,本节继而介绍:WMS依据测量的宽、高,通过SurfaceFlinger代理对象创建视图。这个过程有点类似于笔者之前一篇博客通过SurfaceFlinger代理类创建纯色视图,读者可以通过该博客加深对本章节的理解。创建SurfaceControl的部分代码如下所示

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private int createSurfaceControl(SurfaceControl outSurfaceControl,
            SurfaceControl outBLASTSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
        ...
        WindowSurfaceController surfaceController;
        try {
            ...
            //创建用于显示的SurfaceControl
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally {
            ...
        }
        if (surfaceController != null) {
            //将SurfaceControl拷贝到outSurfaceControl中
            surfaceController.getSurfaceControl(outSurfaceControl);
            ...
        } else {
            ...
        }

        return result;
    }

创建过程简要流程图如下所示:

WindowSurfaceController创建时序图

WindowSurfaceControl创建时序图
其中流程1 - 8介绍创建用于显示的SurfaceControl,流程9介绍 将创建的SurfaceControl对象拷贝并返回到客户进程。

2.2.1 创建SurfaceControl

1.2.21.2.3构造Window父子节点流程中介绍过创建SurfaceControl。本节创建的SurfaceControl对象与上面创建的对象有些不同。
WindowSurfaceControl内部Surface Control创建代码如下:

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceControl.java
WindowSurfaceController(String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
   ...
   final SurfaceControl.Builder b = win.makeSurface()
            .setParent(win.getSurfaceControl())//设置父节点
            .setName(name)
            .setBufferSize(w, h)//视图宽、高
            .setFormat(format)
            .setFlags(flags)
            .setMetadata(METADATA_WINDOW_TYPE, windowType)//窗口属性
            .setMetadata(METADATA_OWNER_UID, ownerUid)
            .setCallsite("WindowSurfaceController");
    mSurfaceControl = b.build();
   ...
}

通过SurfaceControl构造方法可以看到与1.21.3节之间的差异。

  1. WindowSurfaceControler内部的SurfaceControl有宽、高;
  2. WindowSurfaceControler内部的SurfaceControl含有window窗口属性类型;
  3. WindowSurfaceControler内部的SurfaceControl含有窗口像素类型。
    通过建造者模式创建的SurfaceControl,会调用native方法创建native层的SurfaceControl与之对应。
//frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
        ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client;
    //获取SurfaceComposerClient实例
    if (sessionObj != NULL) {
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
    LayerMetadata metadata;
    Parcel* parcel = parcelForJavaObject(env, metadataParcel);
    ...
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
    ...
    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

继续查看client->createSurfaceChecked该方法会发现他与SurfaceControl创建透明视图中创建native层SurfaceControl一致。

2.2.2 拷贝SurfaceControl

上一节创建了有对应窗口大小、有对应层级的SurfaceControl,图形渲染是在用户进程处理的,所以客户进程需要获取WMS创建的SurfaceControl代理。android系统是通过拷贝将SurfaceControl传回客户进程,通过SurfaceControl内部方法

surfaceController.getSurfaceControl(outSurfaceControl)

该方法调用native方法nativeCopyFromSurfaceControl

//frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
    if (surface == nullptr) {
        return 0;
    }
    //拷贝
    sp<SurfaceControl> newSurface = new SurfaceControl(surface);
    newSurface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(newSurface.get());
}

最终会通过relayoutWindow()方法将SurfaceControl带回客户进程。

三、Window显示

3.1 通知WMS DrawState状态

relayoutWindow方法执行完会携带窗口位置、SurfaceControl、InsetsState等数据,View继续进行绘制流程(该流程本文不做介绍),绘制完成后通知WindowManagerService,进行后续流程。
通知WMS客户端绘制完成
这里我们简要介绍下

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
        ...
        if (mDrawState == DRAW_PENDING) {
            mDrawState = COMMIT_DRAW_PENDING;
            layoutNeeded = true;
            ...
        } else if (postDrawTransaction != null) {
            // so apply it now.
            postDrawTransaction.apply();
        }

        return layoutNeeded;
    }

上一节我们介绍流程createSurfaceControl时,会将WindowStateAnimator的绘制状态设置为DRAW_PENDING。Client绘制完进入finishDrawingLocked流程会将
mDrawState设置为COMMIT_DRAW_PENDINGfinishDrawingLocked方法执行完后会调用WindowSurfacePlacer的requestTraveral,进入后续流程会继续更新mDrawState状态。

3.2 窗口动画装载、执行

调用WindowSurfacePlacer的requestTraveral方法,会调用applySurfaceChangesTransaction方法。

applySurfaceChangesTransaction简要时序

流程进入WindowStateAnimator的commitFinishDrawingLocked方法将mDrawState设置为READY_TO_SHOW
窗口动画加载

窗口动画装载时序
这个时候Surface仍然是不显示的步骤17调用scheduleAnimationLocked更新SurfaceControl显示状态。

3.3 更新SurfaceControl显示状态

SurfaceControl显示代码这里就不做分析,这里附上SurfaceControl显示的流程图,有了前面的基础理解这个流程并不复杂:SurfaceControl显示与隐藏是通过调用SurfaceControl内部show、hide方法实现。
SurfaceControl显示时序

SurfaceControl显示时序

总结

至此,本文关于Window创建、测量与显示的分析就结束了。本文分析的是Window的显示,对Activity启动流程有所了解的小伙伴不难发现:流程分析中涉及不少ActivityManagerService架构相关类,感兴趣的小伙伴可以结合ActivityManagerService继续分析Activity的显示

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值