简单分析Window的内部机制


Window并不是实际存在的,它是以View的形式存在,WindowManager中的三个方法addViewupdateViewLayoutremoveView也都是基于View的,这说View才是Window存在的实体。在实际使用中是无法直接访问Window的,对Window的访问必须通过WindowManager,分析Window的内部机制从它的添加、更新、删除入手。

WindowManager是一个接口,它的具体实现是WindowManagerImpl,在WindowManagerImpl中的三个方法实现如下:

//WindowManager
@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);
}

1.Window的添加过程

  • 检查参数是否合法,如果是子View还需要调整一些布局参数

在WindowManagerImpl中,addView最终是交给了WindowManagerGlobal去实现,在WindowManagerGlobal中的addView方法中做的第一步就是检查参数的合法性,代码如下:

//WindowManagerImpl#addView
public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow, int userId) {
    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添加到列表中
@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>();

在WindowManagerGlobal类中创建了以上几个列表,,mViews负责存储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的添加过程

这个过程是在WindowManagerGlobal.addView方法中完成的在addView方法中调用了ViewRootImpl的setView方法,在setView方法的内部会通过requestLayout完成异步刷新请求

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

接着会通过WindowSession最终完成Window的添加过程,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说Window的添加过程试一次IPC的调用

//ViewRootImpl#setView
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的添加,这样一来Window的添加过程就交给WindowManagerService去处理了,在WindowManagerService内部会为每一个应用保留一个单独的Session。

2.Window的删除过程

Window的删除过程也是由WindowManagerGlobal完成的,调用的是removeView方法

//WindowManagerGlobal#removeView
@UnsupportedAppUsage
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来查找待删除的索引,这个查找过程就是建立的数组遍历,数据来源就是之前创建的mViews,然后调用removeViewLocked来做进一步的删除

//WindowManagerGlobal#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,分别是异步删除和同步删除,一般来说removeViewImmediate不常用避免发生意外的错误,removeView的删除是异步删除,这个删除主要依靠的是ViewRootImpl的die方法来完成

//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()方法。异步删除的View并没有立即完成删除操作,所以最后会将其添加搭配mDyingViews列表中,这个列表表示待删除的VIew列表。

真正删除View的逻辑是在dispatchDetachedFromWindow方法中实现,这个方法主要做了四件事情:

  • 垃圾回收相关的工作,比如清除数据和消息、移除回调
  • 通过Session的remove方法删除Window,这依旧是一个IPC的调用,最终调用的是WindowManagerService.removeWindow方法。
  • 调用View的dispatchDetachedFromWindow方法,在内部调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。当View从Window中移除时就会调用onDetachedFromWindow()方法,主要作用就是做一些资源回收的工作,比如终止动画停止线程等。
  • 调用WindowManagerGlobal的doRemoveView方法刷新数据,需要将Window从mView、mParams、mDyingView列表中删除。

3.Window的更新过程

Window的更新过程还是通过WindowManagerGlobal完成的,直接看updateViewLayout方法

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

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

    view.setLayoutParams(wparams);

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

updateViewLayout方法中首先是跟新View的LayoutParams,然后更新ViewRootImpl的LayoutParams,在ViewRootImpl的setLayoutParams会通过scheduleTraversals方法对View重新布局,包括测量、布局、重绘三个过程,除了View的重绘之外ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现,这也是一个IPC过程。

本文转自 [https://juejin.cn/post/7181067713182171195],如有侵权,请联系删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值