Android Window的内部机制

本文源码基于API 26
参考资料 Andriod开发艺术探索
类图
这里写图片描述

流程图
这里写图片描述

Window的添加过程

Window的添加是通过WindowManager的addView()来实现,WindowManager是一个接口,他的真正实现是WindowManagerImpl类
WindowManagerImpl

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

 @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }


    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

可以发现,WIndowManagerImpl并没有实现Window的三种方法,而是全部交给了WindowManagerGlobal来处理。

WindowManagerGlobal
addView()
WindowManagerGlobal的addView()方法主要分为如下几步

1 检查参数是否合法,如果是子Window那么还要调整一些布局参数

if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } 

2 创建ViewRootImpl并将View添加到列表中
下面是WindowManagerGlobal内部的几个比较重要的列表

private final ArrayList mViews = new ArrayList();
    private final ArrayList mRoots = new ArrayList();
    private final ArrayList mParams =
            new ArrayList();
    private final ArraySet mDyingViews = new ArraySet();

mViews存储的是所有的Window所对应的View,
mRoots存储的是WIdnow对应的ViewRootImpl
mParams存储的是所有Window所对应的布局参数,
mDingViews则存储了哪些正在被删除的View对象。或者说是那些已经调用了removeView方法但是删除操作还未完成的Window对象,在addview中通过如下方式将Window的一系列对象添加到列表中。

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

3 通过ViewRootImpl来更新界面并完成Window的添加过程

root.setView(view, wparams, panelParentView);

ViewRootImpl
setView()
在ViewRootImpl中的setView()中会通过requestLayout方法来完成一部刷新请求。

 requestLayout();
 @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

接着会通过WindowSession来完成Window的添加过程。在下面的代码中,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是WIndow的添加过程是一次IPC调用。

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

Session
addToDisplay()


    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

Session内部会通过WindowManagerService来实现Window的添加。

Window的删除过程
WindowManagerGlobal
findViewLocked()
Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。下面是WindowManagerGlobal的removeView方法:

 public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

在此方法中,首先通过findViewLocked方法来查找待删除的View的索引,然后通过removeViewLocked方法来做进一步的删除。

removeViewLocked

 private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

在此方法中,通过ViewRootImpl来完成删除操作。
ViewRootImpl
die()

boolean die(boolean immediate) {
        // Make sure we do execute immediately if we are in the middle of a traversal or the damage
        // done by dispatchDetachedFromWindow will cause havoc on return.
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }

        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } else {
            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }

首先会判断是否是异步删除,
如果是异步删除,就直接发送一个MSG_DIE的消息。ViewRootImpl中的Handler会处理此消息并调用doDie方法。
如果是同步删除,则直接调用doDie方法。
在doDie方法中会调用dispatchDetachedFromWindow方法。

最后会调用WindowManagerGlobal的doRemoeView方法,将当前Window所关联的这三类对象从列表中移除

void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
            doTrimForeground();
        }
    }

dispatchDetachedFromWindow()
在dispatchDetachedFromWindow方法中主要做了如下几件事

1 释放对象,清楚数据和消息,移除回调

        mView.assignParent(null);
        mView = null;
        mAttachInfo.mRootView = null;

        mSurface.release();

        if (mInputQueueCallback != null && mInputQueue != null) {
            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
            mInputQueue.dispose();
            mInputQueueCallback = null;
            mInputQueue = null;
        }
        if (mInputEventReceiver != null) {
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
        }

2 通过Session的remove()方法移除window

  mWindowSession.remove(mWindow);

3 调用View的dispatchDetachedFromWindow方法

 if (mView != null && mView.mAttachInfo != null) {
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
            mView.dispatchDetachedFromWindow();
        }

调用View的dispatchDetachedFromWindow方法,并在dispatchDetachedFromWindow方法的内部会调用onDetachedFromWindow和onDetachedFromWindowInternal方法。onDetachedFromWindow方法一般会做一些资源的回收等。

Window的更新过程
同样还是来看WindowManagerGlobal的updateViewLayout方法
WindowManagerGlobal
updateViewLayout()
1 首先更新View的LayoutParams

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

2 再更新ViewRootImpl的LayoutParams

 synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }

在ViewRootImpl中经过一系列的调用,setLayoutParams最终会调用performTraversals方法.

ViewRootImpl
performTraversals()
在此方法中来对View重新测量,布局,绘制。

测量

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

布局

performLayout(lp, mWidth, mHeight);

绘制

performDraw

在测量,布局,绘制之前调用了relayoutWindow方法。
relayoutWindow()
在relayoutWindow方法中,会通过WindowSession来更新Window的视图,这个过程最终通过WindowManagerService的relayoutWindow方法来实现。

 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,
                mPendingMergedConfiguration, mSurface);

你可能感兴趣的:(源码)