8.Window与WindowManager

1.概述

  1. 所有视图都通过Window来呈现,包括Activity/Dialog/Toast等,Window是View的直接管理者;
  2. WindowManager是外界访问Window的入口;
  3. Window的具体实现在WindowManagerService中,WindowManage与WindowManagerService的交互式IPC过程;
  4. window分为3个层级,WindowManager.LayoutParams.type参数来控制。

WindowManager.LayoutParams.type的取值范围
应用window的层级:1~99
子window的层级:1000~1999
系统window的层级:2000~2999 (需要申请权限)

2.关键类图

在这里插入图片描述

3.重点关注WindowManager的3个方法

addView / updateViewLayout / removeView

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

/**
* The interface that apps use to talk to the window manager.
**/
public interface WindowManager extends ViewManager {

	...
	
}

3.1 添加Window的过程

  1. 检查参数是否合法,若是子Window还需进一步调整布局参数;
  2. 创建ViewRootImpl并将View添加到列表中,桥接模式由WindowManagerGlobal来完成;
  3. 通过ViewRootImpl的setView方法完成更新界面并完成Window的添加过程。

WindowManager.addView
-> WindowManagerGlobal.addView
-> ViewRootImpl.setView
-> WindowManagerService

3.1.1 WindowManager#addView()

WindowManager是一个接口,实现类为WindowManagerImpl.

WindowManagerImpl#addView()

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ...
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    ...
}

WindowManagerImpl的实现是依赖WMG。

3.1.2 WindowManagerGlobal#addView()

public final class WindowManagerGlobal {
	...
	// 保持view的集合以及参数
    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>();
    ...
    
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
			root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
            	...
            }
}

1.WMG维护4个重要的集合:mViews, mRoots, mParams, mDyingViews
2.在WMG#addView()方法维护前面3个集合,
3.在WMG#removeView()方法中维护mDyingViews

3.1.3 ViewRootImpl#setView()

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    	...
                    requestLayout();
                    ...
                try {
                	...
                	// 说明1.mWindowSession是一个Binder类型
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
                	...
                } finally {
                	...
                }
				...
                // 说明2. 接收触屏事件
                // Set up the input pipeline.
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}

ViewRootImpl的setView方法主要完成2件事

  1. 完成view的渲染
    mWindowSession是Session对象,是通过AIDL的方式调用WMS的openSession()方法获取的。
  2. 接收触屏事件,ViewRootImpl - DecorView - window.cb.dispatchTouchEvent - cb就是Activity - PhoneWindow - DecorView, 可见触屏事件的传递是从DecorView路过Activity,最后又回到DecorView。
mWindowSession = WindowManagerGlobal.getWindowSession();

WMG.getWindowSession()

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // 说明1
                    IWindowManager windowManager = getWindowManagerService();
                    // 说明2
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
  1. getWindowManagerService()就是标准的AIDL调用,返回WMS
  2. 获取了WMS接口实例,然后调用WMS#openSession(),返回Session对象

Session#addToDisplay

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

mService就是WMS,添加window的工作从这里传到WMS。

Activity-Window-View

  1. activity#setContentView(layoutResID)
  2. PhoneWindow#setContentView(layoutResID)
  3. 初始化DecorView,它是FrameLayout的子类
    installDecor()
    mLayoutInflater.inflate(layoutResID, mContentParent);

DecorView-ViewRootImpl

  1. Activity的onResume方法将DecorView添加到windowManager
    ActivityThread#handleResumeActivity(…)
    具体几处体现如下:
    View decor = r.window.getDecorView();
    ViewManager wm = a.getWindowManager();
    wm.addView(decor, l);
  2. 进入window的添加过程 WMG-ViewRootImpl-WMS

3.2 删除Window的过程

  1. 找到待删除View的索引index;
  2. removeViewLocked方法内部通过ViewRootImpl的die方法完成,并将view加到mDyingViews列表中。

WindowManager.removeView
-> WindowManagerGlobal.removeView
-> ViewRootImpl.die

3.3 更新Window的过程

  1. 更新View的LayoutParams并替换老的LayoutParams;
  2. 更新ViewRootImpl中的LayoutParams.

WindowManager.updateViewLayout
-> WindowManagerGlobal.updateLayoutView
-> ViewRootImpl.setLayoutParams

4. 总结

  1. 一个Activity对应一个PhoneWindow;
    PhoneWindow是在activity.attach()内创建

    Activity - PhoneWindow - ViewRootImpl
    它们3者是一一对应的

  2. WMG是单例获取的,所以一个进程中只有一个;
  3. 一个PhoneWindow对应一个ViewRootImpl;
    WM#addView() -> WMG#addView() 此方法内会创建 ViewRootImpl对象,并保存在mRoots集合中
  4. ViewRootImpl#setView()方法很重要,完成view渲染、接收触屏事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值