热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WindowManager体系总结

  WindowManager是Android中一个比较复杂的知识体系,今天来总结总结这方面的体系知识,为了便于理解,文章会使用通俗易懂的语言结合XMind图和一些源码进行一步步分析。

    WindowManager是Android中一个比较复杂的知识体系,今天来总结总结这方面的体系知识,为了便于理解,文章会使用通俗易懂的语言结合XMind图和一些源码进行一步步分析。

一、Window、WindowManager和WMS关系

    Window是这三者的核心,起到了桥梁的作用,她是一个抽象类,主要定义了一些对Window操作的抽象方法。WindowManager主要是管理Window是一个接口类,他继承自ViewManager接口,主要定义了一些对View的操作和一些常量,例如:添加View,移除View,删除View。WMS是WindowManagerServer的缩写,主要是连接WindowManager与Window之间的Binder的通信,即WindowManager通过WMS操作Window。


二、Window的属性

    Window的属性是指Window怎样通过WMS管理Window,这些管理的规则被定义在被定义在WindowManager的内部类LayoutParams中,了解Window的属性能够更好的理解WMS的内部原理。这里主要介绍Window的类型,Window的Flag,Window的软键盘相关模式三大内容。

1、Window的类型

    Window的类型大致包括三类,应用程序窗口,子窗口,系统窗口。每个大类又包含了很多种类型,它们都定义在WindowManager的静态内部类LayoutParams中。接下来对这三种窗口类型进行说明。

    (1)应用程序窗口:对应一个Activity,作用于Activity内部,在WindowManager定义了应用程序窗口TYPE值得类型范围1到99,数值越大说明该Window的等级越高,越在上面显示。
    (2)子窗口:不能独立的存在,必须依附于任何类型的父窗口。例如Dialog等,子窗口的TYPE值范围是1000到1999。
    (3)系统窗口:系统窗口的等级最高为,TYPE值范围为2000到2999。例如Toast、入法窗口、状态栏、系统错误窗口等等。

    注意:窗口可以叠加,显示次序根据TYPE值得顺序排列,依次为:系统窗口,子窗口,应用程序窗口。高的等级可以覆盖低的等级。

2、Window的标志

    Window的标志也就是Flag,用于控制Window的显示,同样被定义在WindowManager的内部类LayoutParams中,包括了很多种这里不进行一一列举了,设置通过通过Window的addFlags方法设置:

    (1)方法一

window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏

    (2)方法二

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

    注意:一般的弹窗需要依附于activity,在activity中弹窗可以不用设置flag,依附于应用程序窗口。但是在服务、广播中没有父窗口可以依附。所以不能简单使用这种方式,需要采用系统的弹窗,他的优先级很高,覆盖于应用界面的最高层,并且要设置setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),否则会崩溃。

3、Window的软键盘相关模式

    Android中窗口与窗口的叠加死很常见的,但是如果叠加的是软键盘很可能就遮盖住了输入框,导致无法输入,这种体验是很差的这时候就需要设置Window的软键盘模式。有两种设置方式,一种是在清单文件Activity中设置,另一种是在代码里面进行设置。

    (1)android:windowSoftInputMode="stateVisible|adjustResize"

    (2)getWindow().setSoftInputMode(WindowManager.LayoutParams.

SOFT_INPUT_ADJUST_PAN);

      各输入模式的值含义:

       stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置

       stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示

       stateHidden:用户选择activity时,软键盘总是被隐藏

       stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的

       stateVisible:软键盘通常是可见的

       stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态

       adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示

       adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间

       adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分


三、Window的添加过程

    WindowManager对Window的操作包括添加、更新、删除三个过程,他是一个接口继承自ViewManager,通过WMS进行操作。

public interface ViewManager {
void addView(View var1, LayoutParams var2);
void updateViewLayout(View var1, LayoutParams var2);
void removeView(View var1);
}

    这个操作包含了两大部分一个是WindowManager的操作过程,另一个是WMS的操作过程,对于应用程序窗口,子窗口,系统窗口的窗口添加过程会有所不同,但是对于WMS处理部分,添加的过程基本上是一样的。


    WindowManager操作Window实际上是对View的操作,这个过程是通过实现类WindowManagerImpl,添加View是通过addView方法进行添加的,这个类在 /frameworks/base/core/java/android/view/WindowManagerImpl.java下,如下:

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

    可以看出Window的添加过程是通过WindowManagerGlobal进行操作的,WindowManagerImpl并没有涉及到实际的操作。找到/frameworks/base/core/java/android/view/WindowManagerGlobal.java这个类中的addView方法,源码比较长,不贴出来了可以看出主要做了如下:

    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");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
....................................省略部分代码...................................
}

   2、创建ViewRootImpl集合,并且添加View。

private final ArrayList mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =new ArrayList();
private final ArraySet mDyingViews = new ArraySet();
..........................................分割线............................................
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

    3、通过ViewRootImpl的setView调用requestLayout更新界面,最终会调用WindowSession的addToDisplay方法进行绘制,他的内部会调用WMS完成添加。源码地址/frameworks/base/core/java/android/view/ViewRootImpl.java。

(1)View的绘制

@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//View的绘制入口
scheduleTraversals();
}
}

(2)View的添加

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);

   mWindowSession是一个AIDL文件,定义了很多IPC通信通信接口,文件地址/frameworks/base/core/java/android/view/IWindowSession.aidl,真正的实现类是Session类。这个类位于/frameworks/base/services/core/java/com/android/server/wm/Session.java

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

        上面的mService就是WindowManagerService,这样一来,Window的添加请求就会从这交给WMS处理了。至此Window的添加过程分析结束。以上就是Window的添加过程的主要流程,更新和删除和这个类似,这里不再做分析了。


注:转载请注明,尊重他人的劳动成果。



推荐阅读
author-avatar
秋知落叶冷
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有