前面说到,应用程序添加窗口时,会在本地创建一个ViewRoot,然后通过IPC(进程间通信)调用WmS的Session的addWindow请求WmS创建窗口,下面来看看addWindow方法。

addWindow方法定义在frameworks/base/services/java/com.android.server.WindowManagerService.java中,其代码如下所示:

public int addWindow(Session session, IWindow client,

WindowManager.LayoutParams attrs, int viewVisibility,

Rect outContentInsets, InputChannel outInputChannel) {

// 是否有添加权限

int res = mPolicy.checkAddPermission(attrs);

if (res != WindowManagerImpl.ADD_OKAY) {

return res;

}

boolean reportNewConfig = false;

WindowState attachedWindow = null;

WindowState win = null;

synchronized(mWindowMap) {

// Instantiating a Display requires talking with the simulator,

// so don't do it until we know the system is mostly up and

// running.

// 是否存在显示设置

if (mDisplay == null) {

// 若不存在,则获取系统设置

WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

mDisplay = wm.getDefaultDisplay();

mInitialDisplayWidth = mDisplay.getWidth();

mInitialDisplayHeight = mDisplay.getHeight();

// 将Display存放到InputManager中

mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);

reportNewConfig = true;

}

// 是否重复添加

if (mWindowMap.containsKey(client.asBinder())) {

Slog.w(TAG, "Window " + client + " is already added");

return WindowManagerImpl.ADD_DUPLICATE_ADD;

}

// 是否子窗口

if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {

// 若为子窗口

// 返回WmS中存在的对应父窗口,若不存在则返回null

attachedWindow = windowForClientLocked(null, attrs.token, false);

// 若父窗口不存在,则表示添加了错误的子窗口

if (attachedWindow == null) {

Slog.w(TAG, "Attempted to add window with token that is not a window: "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;

}

// 若取得的父窗口也是子窗口,则表示添加了错误的子窗口,从这里来看,貌似窗口只有两层??

if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW

&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {

Slog.w(TAG, "Attempted to add window with token that is a sub-window: "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;

}

}

boolean addToken = false;

// 在WmS中寻找对应的WindowToken

WindowToken token = mTokenMap.get(attrs.token);

if (token == null) {

if (attrs.type >= FIRST_APPLICATION_WINDOW

&& attrs.type <= LAST_APPLICATION_WINDOW) {

// 对于子窗口来说,WmS中必须有对应的Token才能添加

Slog.w(TAG, "Attempted to add application window with unknown token "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_APP_TOKEN;

}

if (attrs.type == TYPE_INPUT_METHOD) {

// 如果是内置的输入方法窗口,WmS中必须有对应的Token才能添加

Slog.w(TAG, "Attempted to add input method window with unknown token "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_APP_TOKEN;

}

if (attrs.type == TYPE_WALLPAPER) {

// 墙纸窗口,WmS中必须有对应的Token才能添加

Slog.w(TAG, "Attempted to add wallpaper window with unknown token "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_APP_TOKEN;

}

// 创建窗口

token = new WindowToken(attrs.token, -1, false);

addToken = true;

} else if (attrs.type >= FIRST_APPLICATION_WINDOW

&& attrs.type <= LAST_APPLICATION_WINDOW) {

// token不为null且是应用窗口

AppWindowToken atoken = token.appWindowToken;

if (atoken == null) {

// appWindowToken值不能为空

Slog.w(TAG, "Attempted to add window with non-application token "

+ token + ". Aborting.");

return WindowManagerImpl.ADD_NOT_APP_TOKEN;

} else if (atoken.removed) {

// 试图使用存在的应用token添加窗口

Slog.w(TAG, "Attempted to add window with exiting application token "

+ token + ". Aborting.");

return WindowManagerImpl.ADD_APP_EXITING;

}

if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {

// No need for this guy!

// 窗口类型不能是应用启动时显示的窗口

if (localLOGV) Slog.v(

TAG, "**** NO NEED TO START: " + attrs.getTitle());

return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;

}

} else if (attrs.type == TYPE_INPUT_METHOD) {

// 对于内置的输入方法窗口,token的windowType值要等于TYPE_INPUT_METHOD

if (token.windowType != TYPE_INPUT_METHOD) {

Slog.w(TAG, "Attempted to add input method window with bad token "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_APP_TOKEN;

}

} else if (attrs.type == TYPE_WALLPAPER) {

// 对于墙纸窗口,token的windowType值要等于TYPE_WALLPAPER

if (token.windowType != TYPE_WALLPAPER) {

Slog.w(TAG, "Attempted to add wallpaper window with bad token "

+ attrs.token + ". Aborting.");

return WindowManagerImpl.ADD_BAD_APP_TOKEN;

}

}

// 创建窗口

win = new WindowState(session, client, token,

attachedWindow, attrs, viewVisibility);

if (win.mDeathRecipient == null) {

// Client has apparently died, so there is no reason to

// continue.

// 客户端已被销毁,所以没必要继续

Slog.w(TAG, "Adding window client " + client.asBinder()

+ " that is dead, aborting.");

return WindowManagerImpl.ADD_APP_EXITING;

}

// 如果是Toast,则此窗口不能够接收input事件

mPolicy.adjustWindowParamsLw(win.mAttrs);

// 判断添加的窗口是单例还是多例

res = mPolicy.prepareAddWindowLw(win, attrs);

if (res != WindowManagerImpl.ADD_OKAY) {

// 是多例则直接返回

return res;

}

// 如果输出的Channel,也即Pipe中的读通道为空

if (outInputChannel != null) {

// 创建通道

String name = win.makeInputChannelName();

InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

win.mInputChannel = inputChannels[0];

inputChannels[1].transferToBinderOutParameter(outInputChannel);

// 在InputManager中注册通道

mInputManager.registerInputChannel(win.mInputChannel);

}

// From now on, no exceptions or errors allowed!

res = WindowManagerImpl.ADD_OKAY;

// 重置当前线程的IPC的ID

final long origId = Binder.clearCallingIdentity();

// 从上述代码中得出是否要添加Token,若是则添加Token添加到WmS中

if (addToken) {

mTokenMap.put(attrs.token, token);

mTokenList.add(token);

}

// 将窗口添加到Session中

win.attach();

// 窗口信息添加到WmS中

mWindowMap.put(client.asBinder(), win);

if (attrs.type == TYPE_APPLICATION_STARTING &&

token.appWindowToken != null) {

// 对于应用启动时显示的窗口,设置token

token.appWindowToken.startingWindow = win;

}

boolean imMayMove = true;

if (attrs.type == TYPE_INPUT_METHOD) {

// 内置的输入方法窗口

mInputMethodWindow = win;

addInputMethodWindowToListLocked(win);

imMayMove = false;

} else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {

// 内置的输入方法对话框窗口

mInputMethodDialogs.add(win);

addWindowToListInOrderLocked(win, true);

adjustInputMethodDialogsLocked();

imMayMove = false;

} else {

// 其他窗口

addWindowToListInOrderLocked(win, true);

if (attrs.type == TYPE_WALLPAPER) {

mLastWallpaperTimeoutTime = 0;

adjustWallpaperWindowsLocked();

} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {

adjustWallpaperWindowsLocked();

}

}

win.mEnterAnimationPending = true;

// 获取系统窗口区域的insets

mPolicy.getContentInsetHintLw(attrs, outContentInsets);

if (mInTouchMode) {

// 用户直接触摸的窗口

res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;

}

if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {

// 应用窗口

res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;

}

boolean focusChanged = false;

if (win.canReceiveKeys()) {

// 窗口需要按键事件

// 更新焦点,将窗口信息写入了InputDispatcher

focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);

if (focusChanged) {

imMayMove = false;

}

}

if (imMayMove) {

// 若需要锁定的话,移动输入方法窗口

moveInputMethodWindowsIfNeededLocked(false);

}

assignLayersLocked();

// Don't do layout here, the window must call

// relayout to be displayed, so we'll do it there.

//dump();

if (focusChanged) {

finishUpdateFocusedWindowAfterAssignLayersLocked();

}

if (localLOGV) Slog.v(

TAG, "New client " + client.asBinder()

+ ": window=" + win);

if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked()) {

reportNewConfig = true;

}

}

// sendNewConfiguration() checks caller permissions so we must call it with

// privilege. updateOrientationFromAppTokens() clears and resets the caller

// identity anyway, so it's safe to just clear & restore around this whole

// block.

final long origId = Binder.clearCallingIdentity();

if (reportNewConfig) {

sendNewConfiguration();

}

Binder.restoreCallingIdentity(origId);

return res;

}

有些东西还没摸明白,后面深入学习后再补一下。

上文还说到,addWindow会将窗口信息写入InputDispatcher,其实在addWindow代码中有体现:

if (win.canReceiveKeys()) {

// 窗口需要按键事件

// 更新焦点,在这里,将窗口信息写入了InputDispatcher

focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);

if (focusChanged) {

imMayMove = false;

}

}         至于如何写入InputDispatcher,下文分析。

android中的add方法,Android入门之addWindow相关推荐

  1. android中的add方法,Android中Fragment怎么addView?

    慕勒3428872 Fragment是Android honeycomb 3.0新增的概念,在Android--Fragment介绍.AndroidFragment使用.Android Fragmen ...

  2. android中checkbox使用方法,Android开发中CheckBox的简单用法示例

    本文实例讲述了Android开发中CheckBox的简单用法.分享给大家供大家参考,具体如下: CheckBox是一种在界面开发中比较常见的控件,Android中UI开发也有CheckBox,简单的说 ...

  3. Android解析xml的方法,Android中解析XML格式数据的方法

    XML介绍:Extensible Markup Language,即可扩展标记语言 一.概述 Android中解析XML格式数据大致有三种方法: SAX DOM PULL 二.详解 2.1 SAX S ...

  4. android颜色值的表示方法android:background=#FFFFFFFF的意思

    android颜色值的表示方法 android:background="#FFFFFFFF"的意思 Android中的颜色值是通过红(Red).绿(Green).蓝(Blue)三原 ...

  5. HashSet中的add()方法( 五 )(详尽版)

    上接 HashSet中的add()方法( 四 )(详尽版) ,我们再重写一下equals()方法来看看是否可以不能存入相同的id: 在学生类中再重写equals()方法: public class S ...

  6. HashSet中的add()方法( 四 )(详尽版)

    上接 HashSet中的add()方法( 三 )(详尽版) ,我们重写一下Student类中的hashCode()方法来看看是否还能不能添加重复的学号了, 在学生类中重写hashCode()方法: p ...

  7. HashSet中的add()方法( 三 )(详尽版)

    上接HashSet中的add()方法( 二 )(详尽版) ,前两篇说的是泛型为String类的add()方法的具体执行过程,此后三篇说说泛型为自定义类的add()方法的具体执行过程: 首先让我们来自定 ...

  8. HashSet中的add()方法( 零 )(详尽版)

    我们知道在使用HashSet集合时,也就是在用HashMap集合,这是因为HashSet的底层是HashMap, public HashSet() {map = new HashMap<> ...

  9. HashSet中的add()方法( 一 )(详尽版)

    让我们用例子来理解add()方法的底层代码吧,Let's go: import java.util.HashSet;public class Test {public static void main ...

最新文章

  1. emacs 探索之五:latex配置
  2. 疯狂位图之——位图实现12GB无重复大整数集排序
  3. 解决IE6下 position的fixed定位问题
  4. JAVA中在某游戏系统有猫狗猪_算法面试题之猫狗队列(java)
  5. ARC122C-Calculator【乱搞,构造】
  6. catv系统主要有哪三部分组成_答案光接入试题(答案)3.12
  7. 金属激光切割机行业调研报告 - 市场现状分析与发展前景预测
  8. docker 容器启动后立马退出的解决方法
  9. 卸载MySQL以及重装卡到Start Services的解决办法(亲测有效,刚重装成功)
  10. Acrobat Pro DC 教程,如何在 PDF 中添加和组织页面?
  11. 吉米多维奇数学分析_《吉米多维奇数学分析习题集》到底是一本怎样的书?
  12. 人工智能语料库技术是什么?来看科普!
  13. dell 恢复介质_戴尔介质恢复选项
  14. 使用百度siteapp开发网站的App-(IOS和Android版本)
  15. MacOs 查看本地IP和Mac地址
  16. 用计算机语言说一局情话,计算机中的情话
  17. python 读取zip包中的数据
  18. 手机里舍不得删的48条短信
  19. 关于大学中软件工程课程的开设问题——不要把实践性科学当作理论性学科来教授...
  20. Redis基础知识+安装+常用命令使用

热门文章

  1. 隐马尔科夫模型C#类库调用示例
  2. 轻松搭建一个Windows SVN服务器
  3. GIS实用小技巧(三)-CASS怎么添加图例?
  4. GIS实战应用案例100篇(二十一)-全国分省、市、县净初级生产力NPP数据制作实战(附代码)
  5. js如何获取html图片,JS/JQuery获取网页或文章或某DIV所有图片
  6. Java Mail+MYSQL+Tomcate+jsp实现企业快信系统
  7. 计算机英语女人英语怎么说,英语时差:计算机和女人
  8. matlab试用版的user id,免费试用MATLAB
  9. c语言指针自定义函数,c语言函数指针定义,指针函数和函数指针的区别
  10. moment 24小时与12小时区别