1, 概述

Android窗口类型主要分成了三大类:

1,应用程序窗口。一般应用程序的窗口,比如我们应用程序的Activity的窗口。

2,子窗口。一般在Activity里面的窗口,比如各种菜单等。

3,系统窗口。系统的窗口,比如输入法,Toast,墙纸等。

WindowManager里面窗口的type类型值定义是一个递增保留的连续增大数值。其实就是窗口的Z-ORDER序列(值越大显示的位置越在上面,需要将屏幕想成三维坐标模式)。

2 WindowManager/LayoutParams

2.1 窗口类型

WindowManager(窗口管理)是如何管理Window呢?

WindowManager是一个接口,首先看看里面的关键定义,

窗口类型,

public int type;

应用程序窗口

FIRST_APPLICATION_WINDOW = 1

第一个应用窗口

TYPE_APPLICATION = 2

应用的默认窗口

LAST_APPLICATION_WINDOW = 99

最后的应用窗口

所有acitivty的窗口的值都在[1,99],默认值是TYPE_APPLICATION,WMS在进行窗口叠加时,会动态的改变activity的值。

子窗口

子窗口的Z序和坐标空间都依赖于Activity窗口

所有子窗口的值都在[1000,1999], WMS在进行窗口叠加时,会动态调整子窗口的值。

系统窗口

FIRST_SYSTEM_WINDOW     = 2000

第一个系统窗口

TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW

状态条

TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1

搜索条

TYPE_PHONE= FIRST_SYSTEM_WINDOW+2

来电显示窗口

TYPE_SYSTEM_ALERT= FIRST_SYSTEM_WINDOW+3

警告对话框

TYPE_KEYGUARD= FIRST_SYSTEM_WINDOW+4

屏保

TYPE_TOAST= FIRST_SYSTEM_WINDOW+5

Toast

TYPE_SYSTEM_OVERLAY= FIRST_SYSTEM_WINDOW+6

TYPE_PRIORITY_PHONE= FIRST_SYSTEM_WINDOW+7

屏幕保护下的来电显示窗口

TYPE_SYSTEM_DIALOG= FIRST_SYSTEM_WINDOW+8

TYPE_KEYGUARD_DIALOG= FIRST_SYSTEM_WINDOW+9

屏幕保护下的对话框窗口

TYPE_SYSTEM_ERROR= FIRST_SYSTEM_WINDOW+10

系统错误窗口

TYPE_INPUT_METHOD= FIRST_SYSTEM_WINDOW+11

输入法窗口

系统窗口远不止上面的表格中所展现的,一共定义了33种。

系统窗口的创建一般不依赖于Activity窗口

所有系统窗口的值都在[2000,2999], WMS在进行窗口叠加时,会动态调整子窗口的值。有些系统窗口只能出现一个,不能添加多个,否则用户会觉得很乱(体验差)。比如输入法窗口,系统状态条窗口等。

2.2 窗口内存缓存类型

public int memoryType; // 保存窗口内存缓存类型

窗口内存缓存一共有4种,

public static final int MEMORY_TYPE_NORMAL = 0; //窗口缓冲位于主内存
public static final int MEMORY_TYPE_HARDWARE=1;//窗口缓冲位于可以被DMA访问,或者硬件加速的内存区域
public static final int MEMORY_TYPE_GPU = 2; //窗口缓冲位于可被图形加速器访问的区域
public static final int MEMORY_TYPE_PUSH_BUFFERS = 3; //窗口缓冲不拥有自己的缓冲区,不能被锁定,缓冲区由本地方法提供

2.3 窗口行为类型

public int flags; //保存窗口的行为
窗口行为类型的行为一共32种,数值都是2的n次幂,所以各种flags可以混合使用,直接按位或是最好的方法。

比如最开始四种行为定义如下,

//Flag:当该window对用户可见的时候,允许锁屏public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;//Flag:让该window后所有的东西都成暗淡public static final int FLAG_DIM_BEHIND        = 0x00000002;//Flag:让该window后所有东西都模糊(4.0以上已经放弃这种毛玻璃效果)public static final int FLAG_BLUR_BEHIND        = 0x00000004;//Flag:让window不能获得焦点,这样用户快就不能向该window发送按键事public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;


2.4 硬件加速类型


public int privateFlags;  // 保存硬件加速行为
一共有11种。定义如下,
public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001;
public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002;
public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;

2.5 窗口输入键盘类型


public int softInputMode;  
一共定义了13种键盘在窗口中的显示方法。
public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
当然, WindowManager的一些其他变量也控制着窗口的显示形式。

3 应用窗口


3.1 一般应用窗口

ActivityThread的主线程ApplicationThread的performLaunchActivity方法会调用Activity的onCreate方法,在onCreate中会调用setContentView等方法加载解析xml资源。从Toast的最简单调用开始,它的调用代码是:
void makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager(); // 获取的是WindowManagerImpl对象wm.addView(mDecor, getWindow().getAttributes()); //  PhoneWindowmWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);}
getAttributes最后获取的默认type类型是TYPE_APPLICATION。

3.2 Dialog

Dialog是一系列XXXDialog的基类,我们可以new任意Dialog或者通过Activity提供的 onCreateDialog(……)、onPrepareDialog(……)和showDialog(……)等方法来管理我们的Dialog,但是究 其实质都是来源于Dialog基类,所以我们对于各种XXXDialog来说只用分析Dialog的窗口加载就可以了。
Dialog构造方法如下,
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {if (createContextThemeWrapper) {if (themeResId == 0) {final TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);themeResId = outValue.resourceId;}mContext = new ContextThemeWrapper(context, themeResId);} else {mContext = context;
//mContext已经从外部传入的context对象获得值(一般是个Activity)!!!非常重要,先记住!!!}mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);final Window w = new PhoneWindow(mContext); // 创建新的WindowmWindow = w;w.setCallback(this);w.setOnWindowDismissedCallback(this);w.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}
看看show方法,
public void show() {if (mShowing) {if (mDecor != null) {if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);}mDecor.setVisibility(View.VISIBLE);}return;}mCanceled = false;if (!mCreated) {dispatchOnCreate(null);}onStart();mDecor = mWindow.getDecorView();if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {final ApplicationInfo info = mContext.getApplicationInfo();mWindow.setDefaultIcon(info.icon);mWindow.setDefaultLogo(info.logo);mActionBar = new WindowDecorActionBar(this);}WindowManager.LayoutParams l = mWindow.getAttributes();//和上面分析的Activity一样type为TYPE_APPLICATIONif ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {WindowManager.LayoutParams nl = new WindowManager.LayoutParams();nl.copyFrom(l);nl.softInputMode |=WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;l = nl;}try {mWindowManager.addView(mDecor, l);
//把一个View添加到Activity共用的windowManager里面去mShowing = true;sendShowMessage();} finally {}}
可以看见Dialog的新Window与Activity的Window的type同样都为TYPE_APPLICATION,上面介绍 WindowManager.LayoutParams时TYPE_APPLICATION的注释明确说过,普通应用程序窗口 TYPE_APPLICATION的token必须设置为Activity的token来指定窗口属于谁。所以可以看见,既然Dialog和 Activity共享同一个WindowManager(也就是上面分析的WindowManagerImpl),而WindowManagerImpl
里面有个Window类型的mParentWindow变量,这个变量在Activity的attach中创建WindowManagerImpl时传入 的为当前Activity的Window,而当前Activity的Window里面的mAppToken值又为当前Activity的token,所以 Activity与Dialog共享了同一个mAppToken值,只是Dialog和Activity的Window(PhoneWindow)对象不同。
为什么一定需要Activity的token 呢,禁止在当前activity中弹出另外一个activity的dialog,因为dialog有时候需要交互的,不能像toast一样(toast无交互).
所以传进去的context对象一定是Activity,如果是其他组件(Service)的context会报错。

4 子窗口

PopWindow是android系统中一个典型的子窗口,它的创建也依赖于Activity。
例如各种菜单(PopupMenu)都是利用PopWindow来实现的。
PopupWindow与Dialog一个不同点是PopupWindow是一个阻塞的对话框,如果你直接在Activity的onCreate等方法中显示它则会报错,所以PopupWindow最好另开启一个新线程去调用。
   PopupWindow一个构造函数如下,
public PopupWindow(View contentView, int width, int height, boolean focusable) {if (contentView != null) {mContext = contentView.getContext();//最终这个mContext实质是ActivitymWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}   // WindowManagersetContentView(contentView); // 初始化赋值setWidth(width);setHeight(height);setFocusable(focusable);}
显示时,最后都会调用showAsDropDown方法,
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {if (isShowing() || mContentView == null) {return;}TransitionManager.endTransitions(mDecorView);registerForScrollChanged(anchor, xoff, yoff, gravity);mIsShowing = true;mIsDropdown = true;final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());// preparePopup(p);final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);updateAboveAnchor(aboveAnchor);invokePopup(p);}
createPopupLayoutParams方法会新建一个WindowManager.LayoutParams对象,然后变量type赋值为mWindowLayoutType,
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
private void invokePopup(WindowManager.LayoutParams p) {if (mContext != null) {p.packageName = mContext.getPackageName();}final PopupDecorView decorView = mDecorView;decorView.setFitsSystemWindows(mLayoutInsetDecor);setLayoutDirectionFromAnchor();mWindowManager.addView(decorView, p);if (mEnterTransition != null) {decorView.requestEnterTransition(mEnterTransition);}}
PopWindow中没有像Activity及Dialog一样new新的Window, 完全使用了Activity的Window与WindowManager。

5 系统窗口

系统窗口的种类很多,以Toast为例来论述,
首先看Toast的构造方法,
public Toast(Context context) {mContext = context;mTN = new TN();mTN.mY = context.getResources().getDimensionPixelSize(com.android.internal.R.dimen.toast_y_offset);mTN.mGravity = context.getResources().getInteger(com.android.internal.R.integer.config_toastDefaultGravity);}
内部类TN构造方法如下,
TN() {// XXX This should be changed to use a Dialog, with a Theme.Toast// defined that sets up the layout params appropriately.final WindowManager.LayoutParams params = mParams;params.height = WindowManager.LayoutParams.WRAP_CONTENT;params.width = WindowManager.LayoutParams.WRAP_CONTENT;params.format = PixelFormat.TRANSLUCENT;params.windowAnimations = com.android.internal.R.style.Animation_Toast;params.type = WindowManager.LayoutParams.TYPE_TOAST;params.setTitle("Toast");params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;}
主要是为WindowManager.LayoutParams中的变量赋值, type赋值为
params.type = WindowManager.LayoutParams.TYPE_TOAST;
显示时,调用TN的handleShow方法,如下,
public void handleShow() {if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView+ " mNextView=" + mNextView);if (mView != mNextView) {// remove the old view if necessaryhandleHide();mView = mNextView;Context context = mView.getContext().getApplicationContext();String packageName = mView.getContext().getOpPackageName();if (context == null) {context = mView.getContext();}mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);// We can resolve the Gravity here by using the Locale for getting// the layout directionfinal Configuration config = mView.getContext().getResources().getConfiguration();final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());mParams.gravity = gravity;if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {mParams.horizontalWeight = 1.0f;}if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {mParams.verticalWeight = 1.0f;}mParams.x = mX;mParams.y = mY;mParams.verticalMargin = mVerticalMargin;mParams.horizontalMargin = mHorizontalMargin;mParams.packageName = packageName;if (mView.getParent() != null) {if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);mWM.removeView(mView);}if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);mWM.addView(mView, mParams);trySendAccessibilityEvent();}}
和前2种窗口内型不同的是,系统窗口不依赖于Activity而存在,并且由系统服务统一管理,只是一个通知提示作用,并不会和Activity进行交互。
在使用Toast时context参数尽量使用getApplicationContext(),可以有效的防止静态引用导致的内存泄漏。

6 addView方法


mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
通过这种方法获取的服务都是WindowManagerImpl对象,其addView方法如下,
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {android.util.SeempLog.record_vg_layout(383,params);applyDefaultToken(params);mGlobal.addView(view, params, mDisplay, mParentWindow);}
直接调用WindowManagerGlobal的
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {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 {// If there's no parent, then hardware acceleration for this view is// set from the application's hardware acceleration setting.final Context context = view.getContext();if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;}}ViewRootImpl root;View panelParentView = null;synchronized (mLock) {// Start watching for system property changes.if (mSystemPropertyUpdater == null) {mSystemPropertyUpdater = new Runnable() {@Override public void run() {synchronized (mLock) {for (int i = mRoots.size() - 1; i >= 0; --i) {mRoots.get(i).loadSystemProperties();}}}};SystemProperties.addChangeCallback(mSystemPropertyUpdater);}int index = findViewLocked(view, false);if (index >= 0) {if (mDyingViews.contains(view)) {// Don't wait for MSG_DIE to make it's way through root's queue.mRoots.get(index).doDie();} else {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}// The previous removeView() had not completed executing. Now it has.}// If this is a panel window, then find the window it is being// attached to for future reference.if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {final int count = mViews.size();for (int i = 0; i < count; i++) {if (mRoots.get(i).mWindow.asBinder() == wparams.token) {panelParentView = mViews.get(i);}}}root = new ViewRootImpl(view.getContext(), display); // 初始化view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);}// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView);} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.synchronized (mLock) {final int index = findViewLocked(view, false);if (index >= 0) {removeViewLocked(index, true);}}throw e;}}
最后的setView方法会调用performTraversals方法完成View的测量,确定位置以及显示,这样一个窗口就显示出来了。

android 窗口类型分析相关推荐

  1. Android窗口管理服务WindowManagerService对壁纸窗口(Wallpaper Window)的管理分析

    在Android系统中,壁纸窗口和输入法窗口一样,都是一种特殊类型的窗口,而且它们都是喜欢和一个普通的Activity窗口缠绵在一起.大家可以充分地想象这样的一个3W场景:输入法窗口在上面,壁纸窗口在 ...

  2. Android WindowManagerService机制分析:窗口的显示层级

    WindowManagerService(以下简称WMS)是Android Framework中一个重要的系统服务,用来管理系统中窗口(Window)的行为.Window是一个抽象的概念,它是一个矩形 ...

  3. android的窗口机制分析------UI管理系统

    Activity可以看做是整个Android系统的人机接口,它提供了一个窗口来绘制UI,每个Activity在启动时,我们都需要给它设置一个Content view,作为Activity所呈现的UI内 ...

  4. 基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程

    基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程在线看Android源代码网址: http://aospxref.com/a ...

  5. Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8570428 通过前面几篇文章的学习,我们知道了 ...

  6. Android窗口管理服务WindowManagerService切换Activity窗口(App Transition)的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8596449 在Android系统中,同一时刻只 ...

  7. Android 系统(51)Android窗口机制

    Android窗口机制(四)ViewRootImpl与View和WindowManager 2017年01月15日 15:25:49 阅读数:259 在前篇第(三)文章中,我们讲到了在DecorVie ...

  8. Android平台安全分析

    摘 要 Android的流行使其已成为众多恶意软件的***目标,针对Android的***.恶意软件和应用层特权提升***等安全威胁不断出现.同时,Android源代码开放的特性也吸引了研究人员的广泛 ...

  9. Android窗口机制(五)最终章:WindowManager.LayoutParams和Token以及其他窗口Dialog,Toast

    转自:https://www.jianshu.com/p/bac61386d9bf 前面几篇文章基本介绍完Activity上的窗口机制,但是我们常见的窗口就还有Dialog,Toast这些,本篇文章就 ...

最新文章

  1. pytorch one-hot转数组
  2. zip(), dict(), itertools.repeat(), list(迭代器)
  3. linux下的常用命令
  4. php中如何将验证码放入页面,如何在php中生成验证码图片
  5. 全网最新Spring Boot2.5.1整合Activiti5.22.0企业实战教程<UEL表达式篇>
  6. 【OpenCV应用】python处理行李图像匹配项目——sift函数
  7. YARN组件详细介绍
  8. 一个FLASH小游戏----Redball3
  9. 查看dll是32还是64
  10. Atitit.软件仪表盘(0)--软件的子系统体系说明
  11. Android编译tcpdump,android 5.0以上使用tcpdump
  12. 免费 在线转换 音频,图像,PDF,视频,文档等格式转换
  13. 华科智标_停车场定位导航反向寻车系统
  14. android程序ping服务器
  15. 把Linux下外设的USB端口号映射到固定的名字
  16. 秋月之谋:5.20黄金原油调整不改,主要选择在周尾!
  17. 样机模板素材哪家强?Top4都在这!
  18. 一、CSS弹性布局[弹性盒子、弹性元素]
  19. 连接硬盘计算机没显示,硬盘连接电脑没反应_硬盘连接电脑不显示
  20. 如何提高CRM的采用率?

热门文章

  1. TCP/IP学习笔记:TCP/IP协议介绍
  2. 什么是企业防火墙,什么是策略?
  3. Google Map API
  4. 真正的宇宙中心?未来科技城、云城或将彻底爆发。逃离深圳,拥抱杭州的启示
  5. 网站、网页的自身优化--(如何让网站被搜索引擎搜索到)
  6. 佳能 c3000 系统设置改语言,win7如何更改语言 win7系统中将电脑语言改成英文的方法步骤介绍...
  7. R语言散点图分类、配色、添加趋势线
  8. javascript实现的淘宝五星评价(版本2)
  9. 斯密特:未来六个月Android全胜iOS
  10. 最近做到一道好题,特来和大家分享一下。