Android Window的内部机制

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

流程图
这里写图片描述

1 Window的添加过程

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

1.1 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来处理。

1.2 WindowManagerGlobal

1.2.1 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<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

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);

1.3 ViewRootImpl

1.3.1 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);

1.4 Session

1.4.1 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的添加。

2 Window的删除过程

2.1 WindowManagerGloba

2.1.1 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方法来做进一步的删除。

2.1.2 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来完成删除操作。

2.2 ViewRootImpl

2.2.1 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();
        }
    }
2.2.2 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方法一般会做一些资源的回收等。

同样还是来看WindowManagerGlobal的updateViewLayout方法

3 Window的更新过程

3.1 WindowManagerGlobal

3.1.1 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方法.

3.2 ViewRootImpl

3.2.1 performTraversals()

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

测量

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

布局

performLayout(lp, mWidth, mHeight);

绘制

performDraw

在测量,布局,绘制之前调用了relayoutWindow方法。

3.2.2 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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值