安卓之window分析

1. Window的内部机制

Window是一个抽象的概念, 每一个window都对应着一个View和一个ViewRootImpl, Window和View通过ViewRootImpl来建立联系, 因此WIndow并不是实际存在的。 它是以view的形式存在。
实际开发中无法直接访问Window, 对Window的访问必须通过WindowManager。

window的添加过程
Window的添加过程需要通过WindowManager的addView来实现, WindowManager类是一个接口,它的真正实现是WindowManagerImpl中的Window三大操作

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }
@Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
@Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

WindowManagerImpl并没有实现Window的三大操作, 而是全部交给了WindowManagerGlobal来处理, WindowManagerGlobal以单例的形式向外提供自己的实例, 在WindowManagerGlobal中有如下一段代码
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();。 这种工作博士是典型的桥接模式, 将所有的操作全部委托给WindowManagerGlobal来实现。 WindowManagerGlobal的addView方法主要分为如下几步

检查参数是否合法, 如果是子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);
        }

创建ViewRootImpl并将View添加到列表中

在WindowManagerGlobal内部有几个列表比较重要

@UnsupportedAppUsage
    private final ArrayList<View> mViews = new ArrayList<View>();
    @UnsupportedAppUsage
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    @UnsupportedAppUsage
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

在上面的生命中, mView储存的是所有Window所对应的View, mRoots储存的所有Window所对应的ViewRootImpl, mParams储存的是所有Window所对应的布局参数, mDyingView则储存了那些正在被删除的View对象, 或者说是那些已经调用removeView方法, 但是删除操作还没完成的Window对象。 在addView中通过如下方式把window的一系列对象添加到列表中:

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

            view.setLayoutParams(wparams);

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

通过 ViewRootImpl来更新界面并完成Window的添加对象:

这个步骤由ViewRootImpl的setView方法来完成, 在setView内部会通过requestLayout来完成异步刷新请求。
scheduleTraversal实际是View绘制的入口

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

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

try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    adjustLayoutParamsForCompatibility(mWindowAttributes);
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                    setFrame(mTmpFrame);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    inputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                }

在session内部会通过WindowManagerService来实现Window的添加, 代码如下所示:

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
                outInsetsState, outActiveControls, UserHandle.getUserId(mUid));
    }

如此
window的添加请求就交给WindowManagerService去处理了, 在WindowManagerService内部会为每一个应用保留一个单独的Session。

Window的删除过程

window的删除也是通过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);
        }
    }

removeView首先通过 findViewLocked来查找待删除的View索引, 这个查找过程就是建立数组遍历, 然后再调用removeViewLocked来作进一步删除。

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

        if (root != null) {
            root.getImeFocusController().onWindowDismissed();
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

removeViewLocked是通过ViewRootImpl来完成删除操作的。 在WindowManager中提供了两种删除接口, removeView和removeViewImmediate, 它们分别表示异步删除和同步删除, Window以免发生意外的错误。 这里主要说异步删除的情况。 具体的删除操作由 ViewRootImpl的die方法来完成。 异步删除的情况下, die方法只是发送了一个请求删除的消息后缀, 并立刻返回了, 这个时候View并没有完成删除操作, 所以最后会将其添加到mDyingViews中, mDyingViews表示待删除的View列表。

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

die方法内部只是作了简单的判断, 如果是异步删除, 那么就发送一个MSG_DIE的消息, ViewRootImpl中的handler会处理此消息并调用doDie方法。 如果是同步删除, 那么就不发消息调用doDie方法。 在doDie内部会调用dispatchDetachedFromWindow方法, 真正删除View的逻辑在dispatchDetachedFromWindow方法内部实现:

  1. 垃圾回收相关工作, 比如清楚数据和消息, 移除回调。
  2. 通过Session的remove方法删除Window: mWindowSession.remove(mWindow); 这是一个IPC过程, 最终会调用WindowManagerService的removeWindow方法。
  3. 调用View的dispatchDetachedFromWindow方法, 在内部会调用View的onDetachedFromWindow()以及 onDetachedFromWindowInternal()。 对于onDetachedFromWindowInternal, 当view从Window中移除时, 这个方法就会被调用,可以在这个方法内部作一些资源回收的工作。
  4. 调用WindowManagerglobal的doRemoveView方法刷新数据,包括mRoots, mParams,以及mDyingViews, 需要将当前Window所关联的这三类对象从列表中删除。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值