前面说到,应用程序添加窗口时,会在本地创建一个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中存在的对应父窗口,若不存在则返回nullattachedWindow = 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中寻找对应的WindowTokenWindowToken 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_METHODif (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_WALLPAPERif (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的IDfinal 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) {// 对于应用启动时显示的窗口,设置tokentoken.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;// 获取系统窗口区域的insetsmPolicy.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()) {// 窗口需要按键事件// 更新焦点,将窗口信息写入了InputDispatcherfocusChanged = 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()) {// 窗口需要按键事件// 更新焦点,在这里,将窗口信息写入了InputDispatcherfocusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);if (focusChanged) {imMayMove = false;}}

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

Android入门之addWindow相关推荐

  1. android中的add方法,Android入门之addWindow

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

  2. android 入门 006(sqlite增删改查)

    android 入门 006(sqlite增删改查) package cn.rfvip.feb_14_2_sqlite;import android.content.Context; import a ...

  3. 【详细】Android入门到放弃篇-YES OR NO-》各种UI组件,布局管理器,单元Activity

    问:达叔,你放弃了吗? 答:不,放弃是不可能的,丢了Android,你会心疼吗?如果别人把你丢掉,你是痛苦呢?还是痛苦呢?~ 引导语 有人说,爱上一个人是痛苦的,有人说,喜欢一个人是幸福的. 人与人之 ...

  4. Android入门(十二)SQLite事务、升级数据库

    原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...

  5. 小猪的Android入门之路 day 1

    小猪的Android入门之路 Day 1 Android相关背景与开发环境的搭建 ------转载请注明出处:coder-pig 本节引言: 随着社会经济的发展,移动互联网的越来越热,手机APP开发显 ...

  6. android 教程概要,Android精通教程-第一节Android入门简介

    前言 大家好,我是 Vic,今天给大家带来Android精通教程-第一节Android入门简介的概述,希望你们喜欢 每日一句 If life were predictable it would cea ...

  7. 我认为的android入门学习策略

    我认为的android入门学习策略 初级阶段要学习的内容 1.自己配置环境.  全部采用做新版本 SDK,ADT,ECLIPSE,JDK 2.编写或者运行教学示例.  2.1这时间主要运行的示例如HE ...

  8. Android入门:通过JSON数据与服务器进行通信

    我们完成"Android入门:通过XML数据与服务器进行通信"同样的功能,只是数据传输使用JSON而不是XML: 注意点: (1)当返回JSON时,content-type为tex ...

  9. Android入门教程:ConstraintLayout约束布局

    原文首发自掘金芦苇APP团队,转载到自己小号上再发一遍~ 翻译By Leelion6.关于 ConstraintLayout 的文章其实已经不少了,不过看到这篇文章写的很有趣,以及在翻译的过程中,感受 ...

最新文章

  1. 不提供账号注销等于耍流氓
  2. 基于visual c++之windows核心编程代码分析(31)SNMP协议编程
  3. 【转】SAP S4HANA Cloud被IDC评为全球SaaS和云ERP系统领导者
  4. 联想超融合平台oracle,联想AIO超融合云一体机解决方案.pdf
  5. 第一句就是定义了一种ptrfun的C++类型
  6. JAVA设计模式初探之桥接模式
  7. 图片相似度——hash算法简介
  8. utilities(matlab)—— 前馈网络权值矩阵初始化方式
  9. informix 如何下载
  10. spss因子分析结果解读_SPSS统计结果P=0.000,我该如何解读呢?
  11. 吴恩达 Deeplearning深度学习笔记v5.7 最新PDF版 免积分下载
  12. Java实现网页滑动验证与短信验证码案例精析
  13. [bzoj 4939][Ynoi 2016]掉进兔子洞
  14. 从物联网到元宇宙 PPT
  15. Android 隐藏状态栏
  16. 面向对象:珍视你的好,一生温柔以待!
  17. 俞敏洪:35岁前如何实现自我增值?
  18. GraphicsLab Project之基于物理的着色系统(Physical based shading)-直接光照
  19. 【Labview】对连续采集数据的一种采样方法
  20. 《青春舞曲》教学设计

热门文章

  1. 分治算法:地毯填补问题
  2. c++ opencv数字图像处理:频率域滤波--高通滤波--高斯高通滤波
  3. Python 使用matplotlib模块连续显示图片
  4. 学计算机的一直对画画感兴趣,小学生电脑绘画比赛总结.doc
  5. Axure RP9 页面弹窗效果
  6. golang 字符串 转 时间类型
  7. 面试,肯定会被拒的十大行为!
  8. 金融贷款行业获客新渠道:贷款行业电销资源如何获取,一手精准客户资源在哪找
  9. Intel XDK 上对Edison + Arduino 编程
  10. qdialog 返回值_PyQt5 中QDialog值传递