android开发浅谈之Window和WindowManager
概述
Window表示窗口的概念,他是一个抽象类,他的真正实现类是PhoneWindow,WindowManager用来对Window进行管理,是外接访问Window的入口,Window操作的具体实现是在WindowManagerService中,WindowMager和WindowManagerService交互是IPC的过程。Android中所有的视图都是附加在Window上上呈现的,不管Activity,Dialog,Toast,他们的视图都是附加在Window上的,因此Window实际上是View的直接管理者。
下面我们来详细的了解Window:
WindwoMagaer来添加一个Window
public static void addView(Context context){
Button button = new Button(context);
button.setText("addWindow");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
0,
0,
PixelFormat.TRANSPARENT);
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.gravity= Gravity.LEFT|Gravity.TOP;
layoutParams.x=100;
layoutParams.y=300;
WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
//WindowManager windowManager = context.getWindowManager();
windowManager.addView(button,layoutParams);
}
这段代码可以添加一个Window,位置在(100,300)处,这里面有俩个参数比较重要分别是,type和flag,下面分别介绍一下这俩个参数:
TYPE 窗口的属性
type参数表示Window的类型,Window有三种类型,分别是Application Window(应用窗口),Sub Window(子窗口)和System Window(系统窗口),每个大类型又包含多个小类型,他们都定义在WindowMager的静态内部类LayoutParams中,下面对这三种类型进行讲解。
- Application Window(应用窗口)
Activity就是典型的应用窗口,应用窗口包含的类型如下:
public static final int TYPE_BASE_APPLICATION = 1;
public static final int TYPE_APPLICATION = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int LAST_APPLICATION_WINDOW = 99;
应用窗口就包括了以上几中类型,其中最上方是起始值,最下方是结束值,也就是说应用窗口的Type值的范围是1-99,这个数值的大小涉及窗口的层级。
- Sub Window(子窗口)
子窗口不能够独立存在,要依附在其他窗口上才行,PopupWindow就属于子窗口,子窗口的定义类型如下:
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;
可以看出子窗口的type值范围是1000-1999。
- System Window (系统窗口)
Toast,输入法窗口,系统音量条窗口,系统错误窗口,都属于系统窗口,系统窗口的类型定义如下:
public static final int FIRST_SYSTEM_WINDOW = 2000;
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
public static final int LAST_SYSTEM_WINDOW = 2999;
窗口的显示次序
上面介绍的Type值越大,就意味着靠用户越近,很显然系统的窗口是最大的,他在应用窗口和子窗口的上方。
ViewManager类
WindowMagaer 所提供的功能很简单,只有常用的三个方法即,添加View,更新View,删除View,这个三个方法定义在ViewManager中,而WindowManager继承自ViewManager
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
Window的内部机制
Window是一个抽象概念,每一个Window都对应一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此Window不是实际存在的,他是以View的形式存在的,在实际是一个中,不能直接访问Window,只有通过WindowManager才能访问。
Window的添加过程
- WindowManager–addView
Window的添加是通过WindowManager的addView方法实现的,我们WindowManager##addView方法作为入口来分析,WindowMagager是一个接口,真正的实现在WindowManagerImpl。
- WindowManagerImpl–addView
frameworks\base\core\java\android\view\WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
android.util.SeempLog.record_vg_layout(383,params);
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
- WindowManagerGlobal–addView
frameworks\base\core\java\android\view\WindowManagerGlobal.java
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");
}
........
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 things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
- ViewRootImpl–setView
ViewRootImpl有很多的职责
View树的根,并管理View树
触发View的测量,布局和绘制
输入时间的中转站
管理Surface
负责与WMS通信
我们继续看一下ViewRootImpl的setView方法
frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
......
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
这个方法首先会调用requestLayout方法来完成一部刷新请求。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
scheduleTraversals实际是View绘制的入口。
- Session–addToDisplay
frameworks\base\services\core\java\com\android\server\wm\Session.java
然后调用mWindowSession.addToDisplay方法,mWindowSession是一个IWindowSession类型的,是一个Binder对象,用于进程间通信,也就是说addToDisplay方法其实是运行在WMS所在的进程system_server进程。
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
addToDisplay方法内部调用了WMS的addWindow方法并将自身也就是Session传入了进去,每个应用程序都会有一个Session,WMS会用ArrayList来保存起来,这样所有的工作都交给了WMS来做。
- WindowManagerService–addWindow
WMS会为这个添加的窗口分配Surface,并确定窗口的显示次序,负责显示界面的是画布Surface,而不是窗口本身,WMS会把Surface交给SurfaceFlinger处理,SurfaceFlinger会把这些Surface混合并绘制到屏幕上
下图为addWindow的时序图:
6.Window的更新过程
Window的更新过程和添加过程是类似的,需要调用WindowManager的updateViewLayout方法.
- WindowManager–updateViewLayout
Window的更新过程是通过WindowManager的updateViewLayout方法实现的,我们WindowManager#updateViewLayout方法作为入口来分析,WindowMagager是一个接口,真正的实现在WindowManagerImpl。
- WindowManagerImpl–updateViewLayout
frameworks\base\core\java\android\view\WindowManagerImpl.java
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
android.util.SeempLog.record_vg_layout(384,params);
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
WindowManagerGlobal–updateViewLayout
frameworks\base\core\java\android\view\WindowManagerGlobal.java
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
- ViewRootImpl–setLayoutParams
frameworks\base\core\java\android\view\ViewRootImpl.java
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
......
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
// Don't lose the mode we last auto-computed.
if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
== WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
& ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
}
mWindowAttributesChanged = true;
scheduleTraversals();
调用ViewRoot的setLayoutParams方法,将更新的参数设置到ViewRootImpl中,setLayoutParams方法最终会调用ViewRootImpl的scheduleTraversals方法,我们看下这个方法.
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mChoreographer翻译为编舞者,用于接受系统的VSync信号,在下一个帧渲染时控制一些操作,mChoreographer的postCallback方法用于添加回调,这个添加的回调,将在下一帧渲染时执行,这个添加的回调指的是TraversalRunnable类型的mTraversalRunnable,如下:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
这个方法内部调用了doTraversal
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
这个方法又调用了performTraversals,这个方法中更新了Window的视图,并且完成了View的绘制流程,measure,layout,draw,这样就完成了View的更新.
下图为updateViewLayout的时序图:
参考资料:
(1)Android 理解Window和WindowManager
https://blog.csdn.net/qq_34760508/article/details/103086195
(2)Android App启动过程
https://juejin.cn/post/6844903933802987528
(3)Android View源码解读:浅谈DecorView与ViewRootImpl
https://blog.csdn.net/a553181867/article/details/51477040
(4)Android View 测量流程(Measure)完全解析
https://blog.csdn.net/a553181867/article/details/51494058
(5)Android View 绘制流程(Draw) 完全解析
https://blog.csdn.net/a553181867/article/details/51570854
(6)Android View 布局流程(Layout)完全解析
https://blog.csdn.net/a553181867/article/details/51524527
本文地址:https://blog.csdn.net/hfreeman2008/article/details/111882109
推荐阅读
-
浅谈Android手机联系人开发之增删查改功能
-
Android 开发艺术探索之Window和WindowManager解析
-
Android开发之进程的种类和介绍
-
Android开发之DatePicker和TimePicker实现选择日期时间功能示例
-
Android开发笔记之 RecyclerView和ScrollView嵌套使用,ListView和ScrollView嵌套使用对比
-
Android开发之OpenGL、OpenGL ES的概念和实例讲解
-
Android开发之拖动条和评分组件用法分析
-
Android开发之ExoPlayer的学习和使用(音频)讲解
-
微信小程序开发之IOS和Android兼容的问题
-
Android开发之android架构学习和使用