Android View的绘制原理

109 阅读16分钟

0、前言

本文基本上是屏幕上内容究竟是怎样画出来的 —— Android View 工作原理详解 - 掘金 (juejin.cn)的内容,前辈确实超强。但由于我基础交叉差,所以有些地方没看懂,所以看了源码,查了资料补全了图,并删除部分我认为有误导的文字解析。仅仅作为我的笔记。核心在于我重制的图。如果觉得分析太麻烦,看标题和副标题即可。重点先理清楚流程。

docimg2.docs.qq.com/image/AgAAE… View绘制流程图.jpg

1、初始化phonewindow和windowmanager(onCreate()之前的attach()方法)

  • 在activity的onCreate等方法调用前,它的attach()方法会被调用。
  • 其中创建一个PhoneWindow对象赋值为mWindow,phoneWindow直接继承自windows类,然后调用setWindowManager() 就有了windowsManger
  • 再用getWindowsManager()获取windowmanager
  • 因此Activity中mWindow变量就是PhoneWindows类的实例,mWindowManger就是WindowManagerImpl类的实例。attach方法主要就是为了初始化这两个变量

2、初始化DecorView,

2.1 onCreate()->PhoneWindow.setContentView()->installDecor()->mDecor = generateDecor(-1);

2.2 通过theme属性值->选择相应布局文件->通过inflate.inflater()方法将布局文件加载并add到mDecorView

2.3 setContentView方法中设置有layout布局文件,通过mLayoutInflater.inflater()压入mDecor中id为content的FragmeLayout

  • onCreate方法,中有一个setContentView方法,我们把布局id传过去。
public void setContentView(@LayoutRes int layoutResID) { 
    getWindow().setContentView(layoutResID); initWindowDecorActionBar(); 
}
  • getWindow获得PhoneWindow,看看其中的setContentView

public void setContentView(int layoutResID) {
    if (mContentParent == null) {// 是否首次调用
        // 初始化 Decor
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// 转场动画,默认 false
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    ...
    } else {
        // 解析布局文件
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
...
}
  • 如果首次调用就installDecor,显而易见就是初始化Decor
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        // 生成 DecorView 对象
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        // 调用 generateLayout 方法
        mContentParent = generateLayout(mDecor);
    ...
    }
}
  • 没有mDecor就创建

2.1、首次调用就generaterLayout(mDecor)

protected ViewGroup generateLayout(DecorView decor) {
    TypedArray a = getWindowStyle();
...
    // 通过 WindowStyle 中设置的各种属性对 Window 进行各种初始化操作
    mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
            & (~getForcedWindowFlags());
    if (mIsFloating) {
        setLayout(WRAP_CONTENT, WRAP_CONTENT);
        setFlags(0, flagsToUpdate);
    } else {
        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
    }
....

    int layoutResource;
    int features = getLocalFeatures();

    // 根据设定好的 features 值获取相应的布局文件并赋值给 layoutResource
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    R.attr.dialogTitleIconsDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else {
            layoutResource = R.layout.screen_title_icons;
        }
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
            && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
        layoutResource = R.layout.screen_progress;
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    R.attr.dialogCustomTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else {
            layoutResource = R.layout.screen_custom_title;
        }
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    R.attr.dialogTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
            layoutResource = a.getResourceId(
                    R.styleable.Window_windowActionBarFullscreenDecorLayout,
                    R.layout.screen_action_bar);
        } else {
            layoutResource = R.layout.screen_title;
        }
    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
        layoutResource = R.layout.screen_simple;
    }

    mDecor.startChanging();
    // 调用 onResourcesLoaded 方法
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    // 在 layoutResource 中根据 id:com.android.internal.R.id.content 获取一个 ViewGroup 并赋值给 contentParent  对象
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }
...
    mDecor.finishChanging();
    // 返回 contentParent
    return contentParent;
}
  • DecorView 的 onResourcesLoaded() 方法:
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    ...
    mDecorCaptionView = createDecorCaptionView(inflater);
    // 解析 layoutResource 文件
    final View root = inflater.inflate(layoutResource, null);
    if (mDecorCaptionView != null) {
        ...
    } else {
        // 作为根布局添加到 mDecor 中
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}
  • 通过com.android.internal.R.styleable.Window中设置的各项属性对Windows进行requestFearture或者setFlags等操作
  • 根据requestFeature()等选择不同的窗口修饰布局文件,得到layoutResource。所以设置全屏的requestFeature()需要在setContentView之前调用。
  • layoutResource值传给DecorView的onResourcesLoaded方法,通过LayoutInflater把布局转化为View作为根视图并将添加到mDecorView
  • mDecor找到id为com.android.internal.R.id.content的ViewGroup并作为返回值返回,一般是FrameLayout
2.1.1generaterLayout简单总结
  • 通过styeable.Window中的属性对Window进行requestFeature或者setflag
  • 通过设置好的feature得到layoutResource
  • layoutResource传给onResourceLoaded方法,通过LayoutInflater把布局转为View作为根视图并添加道mDecorview
  • 再mDecor中查找id为com.android.internal.R.id.content的viewgroup并作为返回值返回

2.2 初始化Decorview,即setContentView 即小结

  • 初始化DecorView,它是实例类型,继承于FragmentLayout
  • 通过theme属性值,选择相应布局文件,通过inflate.inflater()方法将它加载并add到mDecorView
  • setCOntentView方法中设置有layout布局文件,通过mLayoutInflater.inflater()压入mDecor中id为content的FragmeLayout。

3、ViewRootImpl的创建和关联DecorView

3.1、performResume()方法,得到activity,decorView,WindowManger

3.2、WindowManger.addView(DecorView)

3.3、root = new ViewRootImpl

3.4、ViewRootImpl.setView(DecorView)

activity得decorView对象
activity得WindowManger 
WindowManger.addView(DecorView, l);-》WindowManagerGlobal.addView(DecorView, l)
// 创建 ViewRootImpl 
root = new ViewRootImpl(view.getContext(), display);
// 将传过来的 DecorView 添加到 ViewRootImpl 中 
root.setView(DecorView,...);
  • ActivityThread在handlelaunchActivity()方法中间接点用了Activity的attach和onCreate
  • 再handleResumeActivity
  • 执行完performResumeActivity方法之后,会取出ActivityClientRecord中的Activity对象,并得到之前再setContentView流程中初始化好的DecorView对象,将它作为参数传入到ViewManager(接口)类型的vm的addView方法
  • attach方法中,会初始化phoneWindow和windowManager对象,有WindowManagerImpl来是实现
  • 所以activity的getWindowsManager是WindowManagerImpl对象的实例,
@Override
3808      public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
3809              String reason) {
3810      ...
3815          // TODO Push resumeArgs into the activity for consideration
3816          final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
3817          if (r == null) {
3818              // We didn't actually resume the activity, so skipping any follow-up actions.
3819              return;
3820          }
3821  
3822          final Activity a = r.activity;
3823   
3844          if (r.window == null && !a.mFinished && willBeVisible) {
3845              r.window = r.activity.getWindow();
3846              View decor = r.window.getDecorView();
3847              decor.setVisibility(View.INVISIBLE);
3848              ViewManager wm = a.getWindowManager();
3849              WindowManager.LayoutParams l = r.window.getAttributes();
3850              a.mDecor = decor;
3851              l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
3852              l.softInputMode |= forwardBit;
3853              if (r.mPreserveWindow) {
3854                  a.mWindowAdded = true;
3855                  r.mPreserveWindow = false;
3856                  // Normally the ViewRoot sets up callbacks with the Activity
3857                  // in addView->ViewRootImpl#setView. If we are instead reusing
3858                  // the decor view we have to notify the view root that the
3859                  // callbacks may have changed.
3860                  ViewRootImpl impl = decor.getViewRootImpl();
3861                  if (impl != null) {
3862                      impl.notifyChildRebuilt();
3863                  }
3864              }
3865              if (a.mVisibleFromClient) {
3866                  if (!a.mWindowAdded) {
3867                      a.mWindowAdded = true;
3868                      wm.addView(decor, l);
3869                  } else {
3870                  
3874                      a.onWindowAttributesChanged(l);
3875                  }
3876              }
3877  
3878              ...
3926      }
3927  
  • 再来看看addView方法,委托给了mGlobal,是WindowManagerGlobal对象的实例

// 将传过来的 DecorView 添加到 ViewRootImpl 中 root.setView(view, wparams, panelParentView);

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    // mGlobal 是 WindowManagerGlobal 对象的实例
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}


public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    ...
    synchronized (mLock) {
        ...
        // 创建 ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
    try {
        // 将传过来的 DecorView 添加到 ViewRootImpl 中
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        ...
    }
}

  • mViews戴白哦所有Window对应的View,mRoots存储的是所有Window对应的ViewRootImpl对象,mParams存储的是所偶Window对应的布局参数
  • 上述代码可以看到 将Decor所代表的view传入root所代表的ViewRootImpl
  • ViewRootImpl是DecorView的管理者,它负责ViewTree的测量、布局和绘制,以及后面会说到的通过Choreographer 来控制 View Tree 的刷新操作。

4、建立PhoneWindow和WindowManagerService之间的连接

4.1 ViewRootImpl.setView(DecorView)

4.2 mWindowSession.addToDisplay binder跨进程通讯关联到WindowManagerService

4.3 phoneWindow->DecorView->VewRootImpl->mWindowSession->WindowManagerService

  • WMS是所有Windows窗口的管理员,负责Window的添加和删除、Surface的管理和事件的派发,因此每个Activity的PhoneWindow对象如果需要显示等操作,必须经过WMS的交互才行
  • 书接上回,ViewRootImpl的setView set的是DecorView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
    
    
        if (mView == null) {
            mView = view;
               ... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. 
               requestLayout();

            ...
            try {
                ...
                // 2、mWindowSession 通过 Binder 远程调用 WMS 建立联系
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
                ...
            } finally {
                ...
            }
            ...
        }
    }
}

  • 核心在于mWindowSession.addToDisplay,就是mWindowSession通过Binder远程调用WMS建立联系。
  • mWindowSession为IWindowSession的实例,而IWindowSession是一个BInder的CLient代理对象,对应的Server端的实现为Session类。
  • 此在之前代码都是在app进程,而session则是运行在WMS所在的进程(SystemServer进程)中。
  • 也就是说,往Window中添加View(DecorView)的流程就交给WindowManagerService去处理了,app进程的ViewRootImpl想要和WMS通讯则需要借助Binder进制并通过Serssion来进行操作。
  • Sesssion的addToDisplay方法: [services/core/java/com/android/server/wm/Session.java]
release%2Fservices%2Fcore%2Fjava%2Fcom%2Fandroid%2Fserver%2Fwm%2FSession.java "https://android.googlesource.com/platform/frameworks/base/+/refs/heads/nougat-release/services/core/java/com/android/server/wm/Session.java")
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    // mService 即是 WMS 对象
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

[services/core/java/com/android/server/wm/WindowManagerService.java]

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
    ...
    // 创建 WindowState 对象
    WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
    ...
    win.attach();  // 会创建一个SurfaceSession
    mWindowMap.put(client.asBinder(), win); // 将新创建的`WindowState`对象`win`与该窗口相关联的客户端的Binder对象一起存储在`mWindowMap`集合中。这样做的目的是为了能够快速、有效地通过Binder对象找到与之关联的窗口状态。
    ...
    win.mToken.addWindow(win); // 一个 token 下会有多个 WindowState。 其实 token 与 PhoneWindow 是一一对应的。
    ...
}

image.png

image.png

5、与SurfaceFlinger连接

5.1、win.attach()->mSession.windowAddedLocked()->mSurfaceSession = new SurfaceSession()->mNativeClient = nativeCreate();->client = new SurfaceComposerClient();->SurfaceComposerClient::onFirstRef()->SurfaceFlinger::createConnection()

5.2、attch方法-》创建surfaceSession->创建SurfaceComposerClient->通过SurfaceFlinger创建一个client代理,用来两者binder通信

  • 书接上回 win.attach(); // 会创建一个SurfaceSession
  • WindowState的attach方法会调用到Session的windowAddedLocked
  • 里面有个nativeCreate()是一个native方法,主要构造了一个SurfaceComposerClient,它是应用程序与SurfaceFligner沟通的桥梁
  • SurfaceComposerClient指针在第一次使用的时候会调用onFirstRef()
  • 通过SurfaceFlinger 的 createScopedConnection 方法创建了一个 ISurfaceComposerClient 的client对象
  • 这个client实现了ISurfaceComposerClient(继承了Interface)接口,能够跨进程通信 surfaceComposerClient就是通过这个client与SurfaceFligner进行通讯的

除此之外它还可以创建Surface,并且维护一个应用程序的所有Layer

image.png

半路插一个VSYN信号说明

  1. VSYNC回调: 当一个新的VSYNC信号到来时(即显示器的刷新率),Choreographer的onVsync方法会被回调。这个方法中,会调用doFrame方法来处理UI更新。

  2. 查找对应时间戳的消息: 在doFrame方法中,Choreographer会遍历消息队列,并根据时间戳找到对应当前帧的View绘制请求。这些请求可能是由于UI组件的状态改变或绘制命令产生的。

  3. View绘制请求的处理doCallbacks方法会处理这些View的绘制请求。这通常涉及到将绘制命令应用到Canvas上,为View的绘制做准备。

  4. View的绘制流程: 绘制请求的处理最终会进入ViewRootImpl的performTraversals方法。这个方法会执行一系列的UI操作,包括但不限于:

    • onMeasure: 测量View的大小。
    • onLayout: 确定View的位置。
    • onDraw: 实际的绘制操作。在这里,Surface会获取一个mCanvas对象,并传递给DecorView。这意味着onDraw方法可以使用这个Canvas对象来进行实际的绘制操作。

6、申请Surface

6.1、 ViewRootImpl.setView(...)->requestLayout()->scheduleTraversals()->mTraversalRunnable->performTraversals()->mWindowSession.relayout(...,mSurface)->BINDER->WMS.relayoutWindow()->createSurfaceControl()-> surfaceController.getSurface(outSurface);

6.2、它在 ViewRootImpl 中定义的时候就已经进行了初始化java端surface,最后走到了mWindowsSession.relayout(surface),在WMS端,有个Native层的Surface对象,通过getSurface将传进来的Java层surface关联起来

final Surface mSurface = new Surface();

  • ViewRootImpl与WMS、SurfaceFlinger都建立了连接,就等Surface来显示了,那么它是啥时候创建的呢?回到第四步的ViewRootImpl的setView方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
          
            try {
                    // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system.  
                    requestLayout();

   
                ...
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } 
            ...
    }
}

  • 注解处意思就是 WMS 进行关联之前,要确保 View Tree 已经进行了 layout 操作,以接收来自 WMS 的事件。
  • requestLayout() 方法 核心中有scheduleTraversals();那么看这个方法。
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 设置同步障碍,暂停处理后面的同步消息
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 在下一帧到来的时候执行 mTraversalRunnable
        //同步屏障的作用就是在消息队列中设置一个标记,确保所有在屏障之前的消息(包括同步和异步消息)都已处理完毕。这样,当UI线程开始绘制视图时,它知道之前的所有消息都已处理,可以安全地进行绘制,而不必担心其他异步消息可能干扰或中断绘制过程。然后执行`mTraversalRunnable`这个Runnable对象。
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
    }
}
  • 设置同步屏障,暂停处理后面的同步消息。

  • 在下一帧到来的时候执行 mTraversalRunnable,当所有的同步消息已经处理完毕,并且准备好进行UI更新时,会执行mTraversalRunnable这个Runnable对象。其实就是处理完异步消息,优先处理我。 也就是VSYNC来的时候,Choreographer收到,并通知程序绘制下一帧,绘制完之后,choreographer会将其交给SurfaceFlinger进行合成并显示。SurfaceFlinger会将绘制好的帧内容合成并刷入到显示设备中

  • 那么mTraversalRunnable 是一个 Runnable 对象:

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


void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 移除同步障碍
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        ...
        // 正式进入 View 绘制流程
        performTraversals();
         ...
    }
}

private void performTraversals() {
    finalView host = mView; // mView 其实就是 DecorView
    ...
    relayoutWindow(params, viewVisibility, insetsPending);
    ...
    // 执行 Measure 流程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    // 执行 Layout 流程
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ...
    // 执行 Draw 流程
    performLayout();
    ...
}

  • 跟踪 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, mPendingBackDropFrame, mPendingConfiguration,
            mSurface);
    ...
}

  • 又是 Binder 调用。
  • mWindowSession 为 IWindowSession 的实例,而 IWindowSession 是一个 Binder 的 Client 代理对象,对应的 Server 端的实现为 Session 类。在这之前代码都是运行在 app 进程的,而 Session 则是运行在 WMS 所在的进程(即 SystemServer 进程)中。
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, mPendingBackDropFrame, mPendingConfiguration,
            mSurface);
    ...
}
  • 将调用WMS的layout方法,注意最后注意最后一个参数就是 SurfaceView 对象,它在 ViewRootImpl 中定义的时候就已经进行了初始化:final Surface mSurface = new Surface();
  • 再来看 WMS 的 relayoutWindow:
public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            Configuration outConfig, Surface outSurface){ 
    ...
    result = createSurfaceControl(outSurface, result, win, winAnimator);  
    ...
}

private int createSurfaceControl(Surface outSurface, int result, WindowState win,WindowStateAnimator winAnimator) {
    ...
    surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
    ...
    surfaceController.getSurface(outSurface);
}

  • 首先调用WindowStateAnimator的createSurfaceLocked生成一个真正优先的Surface对象,这个对象在Native层里,接着getSurface方法将Java层的Surface与其关联起来

7、正式绘制View

7.1 申请了Surface之后,ViewRootImpl的performTraversal()方法会继续执行onMeasure测量,onLayout布局,onDraw绘制

7.2 Surface->canvas->给到DecorView-》onDraw就有Canvas来操作surface

image.png

7.1 理解MeasureSpec

32位int值,高两位表示SpecMode,低30位表示SpecSize。 MeasureSpec举例子篇 - 掘金 (juejin.cn)

image.png

7.2 Measure分析

  • 回到 performTraversals() 方法中,获取到 DecorView 的 MeasureSpec 后接着会调用 performMeasure() 方法:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    ...
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
}

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}


 public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}


image.png

8、显示View

8.1、handlerResumeActivity开始执行wm.addView(),方法之前,DecorView依旧是不可见。即使过了Measure、layout、Draw流程。View仍然没有显示在屏幕上。看看Activity的makeVisible()方法

View 的Measure layout和draw。在前面是handlerResumeActivity中的wm.addView为源头。回顾下handlerResumeActivity

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    ...
    // 最终会调用 onStart() 和 onResume() 等方法
    r = performResumeActivity(token, clearHide, reason);

    if (r != null) {
        final Activity a = r.activity;
        ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            // 获取 DecorView 
            View decor = r.window.getDecorView();
            // 将 DecorView 设置成不可见
            decor.setVisibility(View.INVISIBLE);
            // 获取 ViewManager,这里是 WindowManagerImpl 实例
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            ...
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                // 标记设置为 true
                a.mWindowAdded = true;
                // 调用 WindowManagerImpl 的 addView 方法
                wm.addView(decor, l);
            } 
        } else if (!willBeVisible) {
            ...
        }
        ...
        if (!r.activity.mFinished && willBeVisible
                && r.activity.mDecor != null && !r.hideForNow) {
            ...
            if (r.activity.mVisibleFromClient) {
                // 调用 makeVisible 方法将 DecorView 设置为可见
                r.activity.makeVisible();
            }
        }
        ...
    } else {
        try {
            // 在此过程出现异常,则直接杀死 Activity
            ActivityManagerNative.getDefault()
                .finishActivity(token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

  • wm.addView()方法之前,DecorView依旧是不可见。即使过了Measure、layout、Draw流程。View仍然没有显示在屏幕上。看看Activity的makeVisible()方法
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
  
  • 执行 DecorView 的 setVisibility() 之后,我们的 View 才正式出现在屏幕上!

8、总结

  • 1、Window是一个抽象类,提供了各种窗口操作的方法

  • 2、PhoneWindow是WIndow的唯一实现类,每个Activity都会有一个PhoneWindow实例。

  • 3、DecorView --继承至FrameLayout,setcontentView时,phoneWindow会创建DecorView并与DecorView创建关联

  • 4、PhoneWindow会根据Theme和Feature等将对应的布局文件解析并添加到DecorView中,这些布局中都包含一个id为content的Framelayout

  • 5、setContent方法设置的layout布局文件会被phoneWindow解析并压入到DecorView的id为content的framelayout中

  • 6、View的正式绘制时在ViewRootImpl的performTraversals开始的

  • 7、DecorView于WMS关联之前,首先要进行requestlayour操作,为接收事件做好准备

  • 8、requestLayout会通过Binder调用WMS的relayoutWindow方法,建立Java于Native层中的Surface对象映射

  • onMeasure

    • view重写onMeasure方法。根据布局参数和父View的测量规格计算自己的宽高并保存。
    • 、ViewGroup需要重写OnMeasure方法计算所有子元素的尺寸然后计算自己的尺寸并保存。
  • onLayout

    • View,一般无
    • ViewGoup重写,根据测量值确定所有子元素的位置
  • onDraw

    • view的oDrawe方法的Canvas参数时由Native层中的Surface对象返回过来的
    • view需要重写onDraw方法绘制自身
    • ViewGroup需要重写ondraw方法绘制滋生以及遍历子元素对他们进行绘制
  • 9、Activity的onResume生命周期被调用后,ActivityThread才会调用activity.makeVisible让DecorView可见

View绘制流程图.jpg

总结

  • Window 是一个抽象类,提供了各种窗口操作的方法;
  • PhoneWindow 是 Window 的唯一实现类,每个 Acitvity 中都会有一个 PhoneWindw 实例;
  • DecorView —— 顶级视图,它继承自 FrameLayout,setContentView() 时,PhoneWindow 会创建 DecorView 并与 DecorView 建立关联;
  • PhoneWindow 会根据 Theme 和 Feature 等将对应的布局文件将布局解析并添加到 DecorView 中,这些布局中都包含了一个 id 为 content 的 FrameLayout;
  • setContentView() 方法中设置的 layout 布局文件会被 PhoneWindow 解析并压入 DecorView 中 id 为 content 的 FrameLayout 中;
  • View 的正式绘制是从 ViewRootImpl 的 performTraversals() 方法开始的;
  • DecorView 与 WMS 关联之前,首先要进行 requestLayout 操作,为接收事件做好准备;
  • requestLayout 会通过 Binder 调用 WMS 的 relayoutWindow 方法,建立 Java 层和 Native 层中的 Surface 对象的映射;
  • 单一 View 一般需要重写 onMeasure() 方法根据布局参数和父 View 的测量规格计算自己的宽高并保存;
  • ViewGroup 需要重写 onMeasure() 方法计算所有子元素的尺寸然后计算自己的尺寸并保存;
  • 单一 View 一般不需要重写 onLayout() 方法;
  • ViewGroup 需要重写 onLayot() 方法根据测量的值确定所有子元素的位置;
  • View 的 onDraw 方法的 Canvas 参数是由 Native 层中的 Surface 对象返回过来的;
  • 单一 View 需要重写 onDraw() 方法绘制自身;
  • ViewGroup 需要重写 onDraw() 方法绘制自身以及遍历子元素对它们进行绘制。
  • 在 Activity 的 onResume() 生命周期被调用后,ActivityThread 才会调用 activity.makeVisible() 让 DecorView 可见。

这整个过程可以用下图来粗略表示:

显示原理.jpg