Android视图体系—理解Window概念

前言

在Android视图体系中Window就是一个窗口的概念。Android中所有的视图都是依赖于Window显示的,比如:Activity、Dialog、Toast都是在Window中显示的。

首先来熟悉一下Window的属性。

Window的类型:

  • 应用Window:即Android应用所在的Window,比如Activity对应的Window;
  • 子Windwo:必须依赖于应用Window存在,比如:Dialog;
  • 系统Window:系统级别的Window,比如系统错误窗口、Toast。

Windwo的ZOrder属性,ZOrder主要是在屏幕Z轴的数值,主要用来表示Window的层级,数值大的显示在屏幕上层。其中应用Window的数值范围是199;子Window的数值范围是10001999;系统Window的数值范围是2000~2999。

Window的标志:

Flag描述
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON只要窗口可见,就允许在开启状态上的屏幕上锁屏
FLAG_NOT_FOCUSABLE窗口不需要获取焦点,也不需要接收输入事件,设置的同时FLAG_NOT_TOUCH_MODAL也会被设置
FLAG_NOT_TOUCH_MODAL该窗口只会处理自己区域内的点击事件,区域外的事件将传递给其他窗口
FLAG_NOT_TOUCHABLE窗口不接受任何触摸事件
FLAG_KEEP_SCREEN_ON只要窗口可见,屏幕保持常亮
FLAG_LAYOUT_NO_LIMITS允许屏幕超出窗口之外
FLAG_FULLSCREEN窗口全屏显示,隐藏所有屏幕的装饰窗口
FLAG_SHOW_WHEN_LOCKED窗口可以显示在锁屏窗口中
FLAG_IGNORE_CHEEK_PRESSES当用户的脸贴近屏幕时,不会响应此事件
FLAG_TURE_SCREEN_ON窗口显示是将屏幕点亮

窗口机制

在日常开发中,我们通常接触到关于窗口的类有WindowManager和Window。在查看两者的源码时,可以看到WindowManager是一个接口,Window是一个抽象类。其中WindowManager继承了ViewManager接口,ViewManager中定义了三个方法。

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

可以看到这三个方法是addView(),updateViewLayout(),removeView(),即添加、更新、删除。也就是说WindowManager对Window有三种操作。通过这个三种操作,我们来探究下Window的机制。

Window、WindowManager、WMS

在探究Window的增、改、删之前,先来看看Window、WindowManager、WMS三者的之间的关系。我们知道WMS是一个系统服务,它主要是关系窗口的。在这三者之间WindowMnager与WMS通过Binder进行通信,其中Window的具体操作由WMS承担。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZtXBRcD-1605344544409)(E:\Blog\Image\window-windownager-WMS.png)]

Window的机制

添加一个Window

WIndowManager接口的实现类是WindowManagerImpl,我们直接分析WindowManagerImpl。

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

    @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的增、删、改是由一个mGlobal的成员实现的。这个mGlobal是WindowManagerGlobal。继续分析WindowManagerClobal。

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // 1.检查参数
        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");
        }
    	// ...
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // ...
            // 2.创建ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            // 添加View
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // 3.通过创建ViewRootImpl更新界面
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

在WindowManagerGlobal的addView()方法中可以分为3部分来看。

第一部分是检查参数,主要检查的是View是否为null,Display是否为null,布局参数是否正确。

第二部分是创建了ViewRootImpl,并且保存了相关对象。这里是保存了ViewRootImpl对象、添加的View对象以及布局参数。

第三部分是通过调用ViewRootImpl的setView()方法完成界面的更新。接下来就是开始了View的工作流程,也就是View的绘制过程。

最后再来看下ViewRootImpl的setView()方法。在setView()方法中可以看到:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
     // ...
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout,mInputChannel,
                mTempInsets);
}

这里的mWindowSession是IWindowSession,这是一个Binder对象。最终通过mWindowSession到达WMS中完成了Window的添加过程。

其中添加的具体过程是发生在WMS中,这里就不在具体分析了。

删除一个Window

Window的删除过程我们直接看WIndowManagerGlobal中的removeView()方法。

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

可以看到删除View时先是找到View对应的索引,然后交由removeViewLocked()方法执行删除逻辑。

private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();
        // ...
		boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

这里可以看出主要交给了ViewRootImpl的die()方法完成接下来的删除流程,其中immediate是表示是否立即删除,为true的时候会立即删除;为false的时候会先加入消息队列中。

boolean die(boolean immediate) {
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }
        // ...
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }
void doDie() {
        checkThread();
        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
        synchronized (this) {
            if (mRemoved) {
                return;
            }
            mRemoved = true;
            if (mAdded) {
                // 1.执行删除
                dispatchDetachedFromWindow();
            }
            //...
        // 2.WindowManagerGlobal中的状态更新
        WindowManagerGlobal.getInstance().doRemoveView(this);
    }

在ViewRootImpl中的die()方法主要是区分是否将Window立即删除,接下来删除工作交给了doDie()。在这里有分为两部分。

第一部分是删除Window这里真正的删除了Window。在这里可以看到进行了各种资源的回收,清理数据。然后通过mWindowSession使用WMS删除Window。

void dispatchDetachedFromWindow() {
        mFirstInputStage.onDetachedFromWindow(); 
        removeSendWindowContentChangedCallback();
        destroyHardwareRenderer();
        setAccessibilityFocus(null, null);
        mView.assignParent(null);
        mView = null;
        mAttachInfo.mRootView = null;
        destroySurface();
        if (mInputQueueCallback != null && mInputQueue != null) {
            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
            mInputQueue.dispose();
            mInputQueueCallback = null;
            mInputQueue = null;
        }
        if (mInputEventReceiver != null) {
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
        }
        try {
            // 进入WMS中删除Window
            mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
        }

        // Dispose the input channel after removing the window so the Window Manager
        // doesn't interpret the input channel being closed as an abnormal termination.
        if (mInputChannel != null) {
            mInputChannel.dispose();
            mInputChannel = null;
        }
        mDisplayManager.unregisterDisplayListener(mDisplayListener);
        unscheduleTraversals();
    }

第二部分是在WindowManagerGlobal中删除Window的对象。

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

        if (view != null) {
            InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
            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);
            }
        }
    }
更新Window

还是直接看WindowManagerClobal中的更新方法。

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    //...
        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);
        }
    }

这里的逻辑相对比较简单。首先是更新View的布局参数,然后通过ViewRootImpl执行。在setLayoutParams()方法中还会调用mWindowSession的relayoutWindow()方法来通过WMS更新窗口。

Window中存在的联系

在分析Window机制的过程中,我们会发现几个出现频繁的类,他们在实现Window机制中都承担了重要的工作,比如:WindowManager、WindowManagerGlobal、ViewRootImp、WMSl等。那个他们之间有什么联系?这里就直接总结一下。

  • 一个Window对应一个ViewRootImpl,一个WindowManager;
  • WindowManagerGlobal是在App主线程启动时初始化的,也就是一个App进程对应一个WindowManagerGlobal;
  • 每个App进程对应一个Session代理,通过Session实现与WMS的通信;

总结

Window在Android中是一个抽象的概念,我们熟知的实现类是PhoneWindow,在PhoneWindow中我们会看到有DecorView也就是我们常说的顶级View。

那么总结来说,Window就是承载了视图的一个容器,Android中所有的视图都会通过Window来显示。而Window由WMS来管理实现增、删、改。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值