android中的add方法,Android入门之addWindow
前面说到,应用程序添加窗口时,会在本地创建一个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相关推荐
- android中的add方法,Android中Fragment怎么addView?
慕勒3428872 Fragment是Android honeycomb 3.0新增的概念,在Android--Fragment介绍.AndroidFragment使用.Android Fragmen ...
- android中checkbox使用方法,Android开发中CheckBox的简单用法示例
本文实例讲述了Android开发中CheckBox的简单用法.分享给大家供大家参考,具体如下: CheckBox是一种在界面开发中比较常见的控件,Android中UI开发也有CheckBox,简单的说 ...
- Android解析xml的方法,Android中解析XML格式数据的方法
XML介绍:Extensible Markup Language,即可扩展标记语言 一.概述 Android中解析XML格式数据大致有三种方法: SAX DOM PULL 二.详解 2.1 SAX S ...
- android颜色值的表示方法android:background=#FFFFFFFF的意思
android颜色值的表示方法 android:background="#FFFFFFFF"的意思 Android中的颜色值是通过红(Red).绿(Green).蓝(Blue)三原 ...
- HashSet中的add()方法( 五 )(详尽版)
上接 HashSet中的add()方法( 四 )(详尽版) ,我们再重写一下equals()方法来看看是否可以不能存入相同的id: 在学生类中再重写equals()方法: public class S ...
- HashSet中的add()方法( 四 )(详尽版)
上接 HashSet中的add()方法( 三 )(详尽版) ,我们重写一下Student类中的hashCode()方法来看看是否还能不能添加重复的学号了, 在学生类中重写hashCode()方法: p ...
- HashSet中的add()方法( 三 )(详尽版)
上接HashSet中的add()方法( 二 )(详尽版) ,前两篇说的是泛型为String类的add()方法的具体执行过程,此后三篇说说泛型为自定义类的add()方法的具体执行过程: 首先让我们来自定 ...
- HashSet中的add()方法( 零 )(详尽版)
我们知道在使用HashSet集合时,也就是在用HashMap集合,这是因为HashSet的底层是HashMap, public HashSet() {map = new HashMap<> ...
- HashSet中的add()方法( 一 )(详尽版)
让我们用例子来理解add()方法的底层代码吧,Let's go: import java.util.HashSet;public class Test {public static void main ...
最新文章
- emacs 探索之五:latex配置
- 疯狂位图之——位图实现12GB无重复大整数集排序
- 解决IE6下 position的fixed定位问题
- JAVA中在某游戏系统有猫狗猪_算法面试题之猫狗队列(java)
- ARC122C-Calculator【乱搞,构造】
- catv系统主要有哪三部分组成_答案光接入试题(答案)3.12
- 金属激光切割机行业调研报告 - 市场现状分析与发展前景预测
- docker 容器启动后立马退出的解决方法
- 卸载MySQL以及重装卡到Start Services的解决办法(亲测有效,刚重装成功)
- Acrobat Pro DC 教程,如何在 PDF 中添加和组织页面?
- 吉米多维奇数学分析_《吉米多维奇数学分析习题集》到底是一本怎样的书?
- 人工智能语料库技术是什么?来看科普!
- dell 恢复介质_戴尔介质恢复选项
- 使用百度siteapp开发网站的App-(IOS和Android版本)
- MacOs 查看本地IP和Mac地址
- 用计算机语言说一局情话,计算机中的情话
- python 读取zip包中的数据
- 手机里舍不得删的48条短信
- 关于大学中软件工程课程的开设问题——不要把实践性科学当作理论性学科来教授...
- Redis基础知识+安装+常用命令使用
热门文章
- 隐马尔科夫模型C#类库调用示例
- 轻松搭建一个Windows SVN服务器
- GIS实用小技巧(三)-CASS怎么添加图例?
- GIS实战应用案例100篇(二十一)-全国分省、市、县净初级生产力NPP数据制作实战(附代码)
- js如何获取html图片,JS/JQuery获取网页或文章或某DIV所有图片
- Java Mail+MYSQL+Tomcate+jsp实现企业快信系统
- 计算机英语女人英语怎么说,英语时差:计算机和女人
- matlab试用版的user id,免费试用MATLAB
- c语言指针自定义函数,c语言函数指针定义,指针函数和函数指针的区别
- moment 24小时与12小时区别