一、WMS功能介绍
要想了解WindowManager管理机制,首先我们先介绍WMS是一个非常重要的系统服务。
它支撑着视图相关的各项业务,这非常符合软件设计的单一职责原则,其业务和ActivityManagerService(简称AMS)一起几乎占据了framework业务的半壁江山,可见其重要性。关于WMS的内容实在太多了,这里只简单介绍其大致功能以及启动流程。
WMS的大概功能如下图所示:
image.png这里先简单描述一下各项功能:
**窗口管理:**WMS是窗口管理者,结合WindowManager实现窗口的启动、添加、删除,以及管理窗口的大小、层级等。
**窗口动画:**在窗口切换时,使用窗口动画可以使这个过程看起来更炫更生动,这个窗口动画就是由WMS的动画子系统来负责的,动画子系统的管理者便是WindowAnimator。
**输入系统的中转站:** 触摸设备屏幕上的窗口时会产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,找到最合适的窗口来反馈事件。而WMS是这些窗口的管理者,那自然而然就成为了输入系统的中转站了。
**Surface管理:**窗口并不具备绘制功能,所以每个窗口都需要一个Surface来供自己绘制,WMS就是这个Surface的管理者。
二、windowManager简介
windowManager是Android的系统服务SystemService中的重要一员,用于将View动态添加、移除、更新到window中。
3个方法
// 添加view
public void addView(View view, ViewGroup.LayoutParams params);
// 更新view
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
// 移除
public void removeView(View view);
两要素
View
需要有一个被添加的View。
WindowManager.LayoutParams
WindowManager.LayoutParams也是ViewGroup.LayoutParams的一个子类。 WindowManager.LayoutParams的参数type指定了window的类型,包括如下三种类型:
- Application windows(值从1~99),该类型window的token必须被设置成activity的token(token:标识一个window)。应用内的一些小浮球就是该类型window。
- Sub-windows(值从1000~1999),它关联于另一个顶级的window,该类型window的token必须是关联window的token。对话框就是该类型的window。
- System windows(值为2000~2999),该类型的window通常是系统因特殊目的所使用的,不应该被普通应用所使用,且使用需要申请指定的权限(系统权限中的悬浮窗权限)。系统的通知栏就是该类型window。
WindowManager.LayoutParams的参数flags用于设置各种行为,比如:
- 状态栏透明(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
) - 导航栏透明(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
) - 不获取焦点(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
)(注意:如果设置该属性,window通过setOnKeyListener监听back事件会无效) - 不响应触摸事件(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
)
三、WindowManager工作机制
WindowManager是如何将View呈现在界面的。本文主要包含如下内容:
image.png一、一个悬浮按钮的demo
本demo实现了在屏幕上显示一个悬浮的Button,并可以跟随手指的移动而移动。代码如下:
1 public void drawFloatButton() {
2 requestWindowPermission();
3 final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
4 final Button button = new Button(this);
5 button.setText("button");
6 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
7 WindowManager.LayoutParams.WRAP_CONTENT,
8 WindowManager.LayoutParams.WRAP_CONTENT,
9 0,
10 0,
11 PixelFormat.TRANSPARENT);
12 params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
13 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
14 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
15 params.gravity = Gravity.LEFT | Gravity.TOP;
16 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
17 params.x = 500;
18 params.y = 500;
19 windowManager.addView(button, params);
20 button.setOnTouchListener(new View.OnTouchListener() {
21 @Override
22 public boolean onTouch(View v, MotionEvent event) {
23 int rawX = (int) event.getRawX();
24 int rawY = (int) event.getRawY();
25 switch (event.getAction()) {
26 case MotionEvent.ACTION_MOVE:
27 params.x = rawX;
28 params.y = rawY;
29 windowManager.updateViewLayout(button, params);
30 break;
31 default:
32 break;
33 }
34 return false;
35 }
36 });
37 }
如果是在Android6.0及以上,需要处理权限问题,在AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
并在代码中动态申请
1 private void requestWindowPermission() {
2 //android 6.0或者之后的版本需要发一个intent让用户授权
3 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
4 if (!Settings.canDrawOverlays(getApplicationContext())) {
5 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
6 Uri.parse("package:" + getPackageName()));
7 startActivityForResult(intent, 100);
8 }
9 }
10 }
由于该权限是一个敏感权限,所以启动时系统还会弹出一个界面让用户手动开启该权限:
image.png演示效果如下所示:
10cd.jpeg
该Button是个悬浮按钮,它并不是通过在xml布局文件中加入,然后通过Activity的setContentView方法将其显示在界面中的,而是通过第19行的WindowManager.addView方法实现的。gif中可以看到,该Button可以显示到Title区域,也可以在app退出后(不杀死进程)独立显示在桌面上。手指滑动过程中,通过第29行WindowManager.updateViewLayout更新Button的LayoutParams的坐标参数,不断更新该Button的位置。后续我们会对这两个方法做详细分析。
二、Window相关特性
通过上述的demo,我们可以看到设置了LayoutParams的flags、type变量值,它们都是用来定义Window的特性的。
1、flag
flag变量用于设置window的属性,控制其显示特性,比如设置为不获取焦点、不接受触屏事件、显示在锁屏之上等。在WindowManager.LayoutParams类中定义了很多的flag常量,来丰富Window的功能及属性。
2、type
type变量用于表示Window的类型。系统规定了Window有三种类型,按取值由小到大依次为:
(1)应用Window:对应着一个Activity,要创建应用窗口就必须在Activity中完成。层级范围:1~99
(2)子Window:不能独立存在,需要依附于特定的父Window。比如Dialog,PopWindow,菜单等。层级范围:1000~1999
(3)系统Window:拥有系统权限才能创建的Window。比如Toast、系统状态栏、导航栏、手机低电量提示、输入法Window、搜索条、来电显示等。系统Window是独立于应用程序的,理论上讲应用程序没有权限创建系统Window,只有系统进程才有。层级范围:2000~2999
type的值越大,在Window体系中显示的层级就越高。为了理解这一点,我们了解一下窗口的Z-Order管理。
手机上采用的是层叠式的布局,它是一个三维空间,将手机水平方向作为X轴,竖直方向作为Y轴,垂直于屏幕由里向外的方向为Z轴,所有窗口就按照type值的顺序排列在Z轴上,type值越大Z值也就越大。如下图所示,所以系统Window往往会在上层显示:
image.png
三、WindowManager关系网
1、WindowManager在系统架构中的位置
这里咱们先通过系统架构图来直观看看WindowManager在系统架构中的位置:
image.png
WindowManager在系统架构中的位置
上图中红色边框的“Window Manager”和“Surface Manager”都和Window直接相关,“Surface Manager”不在本文的讨论范围内,这里只关注“Window Manager”,从上图可以看出,它位于Framework层。
2、WindowManager实例的获取
在前面的demo中的第三行,通过如下的方式来获取WindowManager实例:
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
这种方式在获取系统服务的时候非常常见,所以这里顺便看看其代码实现:
1 //=============Activity.java============
2 private WindowManager mWindowManager;
3 private Window mWindow;
4 @Override
5 public Object getSystemService(@ServiceName @NonNull String name) {
6 if (getBaseContext() == null) {
7 throw new IllegalStateException(
8 "System services not available to Activities before onCreate()");
9 }
10 if (WINDOW_SERVICE.equals(name)) {
11 return mWindowManager;
12 } else if (SEARCH_SERVICE.equals(name)) {
13 ensureSearchManager();
14 return mSearchManager;
15 }
16 return super.getSystemService(name);
17 }
18
19 final void attach(...){
20 mWindowManager = mWindow.getWindowManager();
21 }
22
23 //==========Context.java==========
24 public static final String WINDOW_SERVICE = "window";
25
26 //===========Window.java==========
27 private WindowManager mWindowManager;
28
29 public WindowManager getWindowManager() {
30 return mWindowManager;
31 }
32
33 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
34 boolean hardwareAccelerated) {
35 ......
36 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
37 }
38
39 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
40 return new WindowManagerImpl(mContext, parentWindow);
41 }
理解上面的逻辑,需要先知道一个继承关系:Activity extends ContextThemeWrapper extends ContextWrapper extends Context。通过上述代码可以明确了,这里获得的WindowManager的实例,实际上是WindowManagerImpl实例。这种面向接口编程(在接口中定义方法,在实现类中实现)的方式很普遍,这里就不赘述了。
3、WindowManager与ViewManager、WindowManager、WindowManagerImpl、WindowManagerGlobal之间的关系
在分析源码前一定要捋清楚ViewManager、WindowManager、WindowManagerImpl、WindowManagerGlobal之间的关系。下面先把关键代码摆出来:
1 public interface ViewManager
2 {
3 public void addView(View view, ViewGroup.LayoutParams params);
4 public void updateViewLayout(View view, ViewGroup.LayoutParams params);
5 public void removeView(View view);
6 }
7
8 public interface WindowManager extends ViewManager{
9 ......
10 }
11
12 public final class WindowManagerImpl implements WindowManager {
13 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
14 @override
15 public void addView(view,params){
16 mGlobal.addView(...);
17 }
18 public void updateViewLayout(view,params){
19 mGlobal.updateViewLayout(...);
20 }
21 public void remove(view){
22 mGlobal.remove(...);
23 }
24 }
25
26 public final class WindowManagerGlobal {
27 public void addView(...){
28 ......
29 }
30 public void updateViewLayout(...) {
31 ......
32 }
33 public void removeView(...) {
34 ......
35 }
36 }
看到上述这段代码,相信你一定已经清楚了它们之间的关系了。ViewManager是个接口,其中定义了三个方法,在Window体系中添加、更新、移除View的过程看起来比较复杂,但其实都是围绕着这三个方法展开的。WindowManager是个接口,继承了ViewManager,扩展了功能。WindowManagerImpl是个实现类,其中包含了WindowManagerGlobal实例。WindowManagerGlobal则真正完成了addView、updateViewLayout、removeView过程。所以,在demo中我们在使用WindowManager的实例调用addView,updateViewLayout方法时,实际上都是WindowManagerGlobal来完成的。这种方式称为桥接模式,这在系统源码种很常见,Context机制中也是这种方式,桥接模式这里就不展开讲了。
第13行中通过WindowManagerGlobal.getInstance()来获取的实例,这里看看其实现:
1 //=========WindowManagerGlobal.java========
2 private static WindowManagerGlobal sDefaultWindowManager;
3 public static WindowManagerGlobal getInstance() {
4 synchronized (WindowManagerGlobal.class) {
5 if (sDefaultWindowManager == null) {
6 sDefaultWindowManager = new WindowManagerGlobal();
7 }
8 return sDefaultWindowManager;
9 }
10 }
可见,它是通过单例模式的方式对外提供的实例。如果对单例模式比较了解的话,就能看出这种实现方式是有问题的(对比DCL方式),但不明白系统源码为什么要这样实现,可能是系统中调用该实例方法的场景比较简单吧,咱们自己在设计单例模式的时候可不能这样做。
在WindowMangerGlobal.java中维护着四个非常重要的list,这四个list在addView、updateViewLayout、removeView的过程中都会频频出现,理清楚这四个list和这三个方法,理解WindowManager工作机制时会清晰很多,它们在下面的源码中会详细讲到。
1 //=========WindowManagerGlobal.java=========
2 //所有Window对应的View
3 private final ArrayList<View> mViews = new ArrayList<View>();
4 //所有Window对应的ViewRootImpl
5 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
6 //所有Window对应的LayoutParams
7 private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>;
8 //正在被删除的View,或者已经执行了removeView方法但还没有完成删除操作的View
9 private final ArraySet<View> mDyingViews = new ArraySet<View>;
四、addView机制
1 //=========WindowManagerGlobal=========
2 public void addView(View view, ViewGroup.LayoutParams params,
3 Display display, Window parentWindow) {
4 ......
5 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
6 ......
7 ViewRootImpl root;
8 View panelParentView = null;
9 synchronized (mLock) {
10 ......
11 //view不能重复添加,如果要添加需要先removeView,否则抛异常
12 int index = findViewLocked(view, false);
13 if (index >= 0) {
14 if (mDyingViews.contains(view)) {
15 // Don't wait for MSG_DIE to make it's way through root's queue.
16 mRoots.get(index).doDie();
17 } else {
18 throw new IllegalStateException("View " + view
19 + " has already been added to the window manager.");
20 }
21 // The previous removeView() had not completed executing. Now it has.
22 }
23 //子window
24 // If this is a panel window, then find the window it is being
25 // attached to for future reference.
26 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
27 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
28 final int count = mViews.size();
29 for (int i = 0; i < count; i++) {
30 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
31 panelParentView = mViews.get(i);
32 }
33 }
34 }
35 root = new ViewRootImpl(view.getContext(), display);
36 view.setLayoutParams(wparams);
37 mViews.add(view);
38 mRoots.add(root);8
39 mParams.add(wparams);
40 // do this last because it fires off messages to start doing things
41 try {
42 root.setView(view, wparams, panelParentView);
43 } catch (RuntimeException e) {
44 // BadTokenException or InvalidDisplayException, clean up.
45 if (index >= 0) {
46 removeViewLocked(index, true);
47 }
48 throw e;
49 }
50 }
51 }
上述代码中除了关键方法外,注意留意mViews、mRoots、mParams集合的操作。
1 //=========ViewRootImpl.java==========
2 /**
3 * We have one child
4 */
5 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
6 synchronized (this) {
7 if (mView == null) {
8 mView = view;
9 ......
10 mAdded = true;
11 int res; /* = WindowManagerImpl.ADD_OKAY; */
12 // Schedule the first layout -before- adding to the window
13 // manager, to make sure we do the relayout before receiving
14 // any other events from the system.
15 requestLayout();
16 ......
17 try {
18 ......
19 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
20 getHostVisibility(), mDisplay.getDisplayId(),
21 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
22 mAttachInfo.mOutsets, mInputChannel);
23 } catch (RemoteException e) {
24 mAdded = false;
25 mView = null;
26 ......
27 throw new RuntimeException("Adding window failed", e);
28 } finally {
29 ......
30 }
31 ......
32 }
33 }
34 }
这里简单截取了一段UML图,读者明白其作用就可以了,想深入研究的可以去这篇文章看看。
image.png第19行的mWindowSession.addToDisplay(...)方法,addView的实际逻辑处理就在这里面。我们继续分析mWindowSession和addToDisplay(...)的逻辑:
1 //========ViewRootImpl.java=========
2 public final class ViewRootImpl{
3 final IWindowSession mWindowSession;
4 public ViewRootImpl(...){
5 ......
6 mWindowSession = WindowManagerGlobal.getWindowSession();
7 ......
8 }
9 }
10
11 //=======WindowManagerGlobal.java==========
12 public static IWindowSession getWindowSession() {
13 synchronized (WindowManagerGlobal.class) {
14 if (sWindowSession == null) {
15 try {
16 ......
17 IWindowManager windowManager = getWindowManagerService();
18 sWindowSession = windowManager.openSession(
19 new IWindowSessionCallback.Stub() {
20 @Override
21 public void onAnimatorScaleChanged(float scale) {
22 ValueAnimator.setDurationScale(scale);
23 }
24 },
25 ......
26 } catch (RemoteException e) {
27 throw e.rethrowFromSystemServer();
28 }
29 }
30 return sWindowSession;
31 }
32 }
33
34 public static IWindowManager getWindowManagerService() {
35 synchronized (WindowManagerGlobal.class) {
36 if (sWindowManagerService == null) {
37 sWindowManagerService = IWindowManager.Stub.asInterface(
38 ServiceManager.getService("window"));
39 ......
40 }
41 return sWindowManagerService;
42 }
43 }
44
45 //============WindowManagerService.java=======
46 @Override
47 public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
48 IInputContext inputContext) {
49 if (client == null) throw new IllegalArgumentException("null client");
50 if (inputContext == null) throw new IllegalArgumentException("null inputContext");
51 Session session = new Session(this, callback, client, inputContext);
52 return session;
53 }
54
55 public int addWindow(Session session, IWindow client, int seq,
56 WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
57 Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
58 InputChannel outInputChannel) {
59 //在wms中真正实现
60 ......
61 }
62
63 //===========Session.java=========
64 final WindowManagerService mService;
65 public Session(WindowManagerService service, ......) {
66 mService = service;
67 ......
68 }
69
70 @Override
71 public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
72 int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
73 Rect outOutsets, InputChannel outInputChannel) {
74 return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
75 outContentInsets, outStableInsets, outOutsets, outInputChannel);
76 }
这里面的代码逻辑很容易理解,最后是把addView的工作交给了WMS的addWindow方法,所以真正添加view的逻辑是在WMS中完成的。这里面逻辑比较复杂繁琐,就不继续深入了,当目前为止就已经清楚整个流程了。
五、updateViewLayout更新机制
1 //============WindowManagerGlobal.java==========
2 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3 ......
4 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
5 view.setLayoutParams(wparams);
6 synchronized (mLock) {
7 int index = findViewLocked(view, true);
8 ViewRootImpl root = mRoots.get(index);
9 mParams.remove(index);
10 mParams.add(index, wparams);
11 root.setLayoutParams(wparams, false);
12 }
13 }
这里面对mParams进行了操作,将旧有的LayoutParams进行了替换。第11行执行了更新逻辑:
1 //=============ViewRootImpl.java==========
2 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
3 ...... //对新的LayoutParams参数做一些操作
4 scheduleTraversals(); //调用绘制流程
5 }
从上述过程可以发现更新过程相对比较简单,更新view的过程简单来说就是,将新的LayoutParams替换掉旧的,并启用绘制流程。
六、removeView移除机制
1 //============WindowManagerImpl.java==========
2 @Override
3 public void removeView(View view) {
4 mGlobal.removeView(view, false);
5 }
6 @Override
7 public void removeViewImmediate(View view) {
8 mGlobal.removeView(view, true);
9 }
WindowManagerImpl类中提供了两个方法用于移除view,从方法名称可以推测其差别在于Immediate,也就是是否立即移除的意思。
1 //==========WindowManagerGlobal.java=========
2 public void removeView(View view, boolean immediate) {
3 ......
4 synchronized (mLock) {
5 int index = findViewLocked(view, true);
6 View curView = mRoots.get(index).getView();
7 removeViewLocked(index, immediate);
8 ......
9 }
10 }
11
12 private void removeViewLocked(int index, boolean immediate) {
13 ViewRootImpl root = mRoots.get(index);
14 View view = root.getView();
15 ......
16 boolean deferred = root.die(immediate);
17 if (view != null) {
18 view.assignParent(null);
19 if (deferred) {
20 mDyingViews.add(view);
21 }
22 }
23 }
24
25 //========ViewRootImpl.java=========
26 private final static int MSG_DIE = 3;
27 final ViewRootHandler mHandler = new ViewRootHandler();
28 /**
29 * @param immediate True, do now if not in traversal. False, put on queue and do later.
30 * @return True, request has been queued. False, request has been completed.
31 */
32 boolean die(boolean immediate) {
33 // Make sure we do execute immediately if we are in the middle of a traversal or the damage
34 // done by dispatchDetachedFromWindow will cause havoc on return.
35 if (immediate && !mIsInTraversal) {
36 doDie();
37 return false;
38 }
39 ......
40 mHandler.sendEmptyMessage(MSG_DIE);
41 return true;
42 }
43
44 final class ViewRootHandler extends Handler {
45 ......
46 @Override
47 public void handleMessage(Message msg) {
48 switch (msg.what) {
49 case MSG_DIE:
50 doDie();
51 break;
52 ......
53 }
54 }
55 }
如上代码验证了之前的猜想,removeView(View)和removeViewImmediate(View)的区别确实就在于是否立即移除。如果调用removeView,会通过handler来调用doDie(),而我们知道handler对应了一个MessageQueue的,需要排队等待执行的,这样就实现了延后执行。而如果调用removeViewImmediate,如果当前没有执行view的遍历,那就直接调用doDie()了。
1 //==========ViewRootImpl.java========
2 void doDie() {
3 ......
4 synchronized (this) {
5 if (mRemoved) {
6 return;
7 }
8 mRemoved = true;
9 if (mAdded) {
10 dispatchDetachedFromWindow();
11 }
12 ......
13 mAdded = false;
14 }
15 WindowManagerGlobal.getInstance().doRemoveView(this);
16 }
17 //移除的主要逻辑都在该方法内完成
18 void dispatchDetachedFromWindow() {
19 mView.dispatchDetachedFromWindow();
20 ......
21 try {
22 mWindowSession.remove(mWindow);
23 } catch (RemoteException e) {
24 }
25 ......
26 unscheduleTraversals();//停止绘制View
27 }
28
29 //======View.java======
30 void dispatchDetachedFromWindow() {
31 ......
32 onDetachedFromWindow();
33 ......
34 }
35
36 @CallSuper
37 protected void onDetachedFromWindow() {
38 //在view从window移除后会回调该方法,可以在其中做一些资源回收的操作,如终止动画、停止线程等。
39 }
40
41 //========WindowManagerGlobal.java=======
42 //该方法主要用于刷新数据
43 void doRemoveView(ViewRootImpl root) {
44 synchronized (mLock) {
45 final int index = mRoots.indexOf(root);
46 if (index >= 0) {
47 mRoots.remove(index);
48 mParams.remove(index);
49 final View view = mViews.remove(index);
50 mDyingViews.remove(view);
51 }
52 }
53 ......
54 }
55
56 //==========Session.java=======
57 @Override
58 public void remove(IWindow window) {
59 mService.removeWindow(this, window);
60 }
61
62 //==========WindowManagerService.java=======
63 void removeWindow(Session session, IWindow client) {
64 synchronized(mWindowMap) {
65 WindowState win = windowForClientLocked(session, client, false);
66 if (win == null) {
67 return;
68 }
69 win.removeIfPossible();
70 }
71 }
上述doDie()过程比较容易理解,第22行,参考addView中的逻辑分析可知,这里也是IPC方式,流程最终进入到了WMS中的removeWindow方法,同样到这里咱们不继续往下深入了。上述流程中,mRoots、mParams、mViews、mDyingViews四个集合也做了相应的操作。
上面讲到的三个主要方法中,可以看到它们都对mRoots、mParams、mViews、mDyingViews进行了刷新。
文末
学到越深,发现需要学的越多!本文目的也是掌握Windowmanager管理window的流程。关于更多Android学习,我这里整理出一套系统性的学习资料Android进阶学习供大家深入了解进阶, 总而言之来讲,WindowManager 是一个非常棒的窗口管理软件。
它可以记住与恢复程序与窗口的位置与大小。很多程序不记得它们在会话中间的位置与大小,甚至 Windows 资源管理器也不总是将窗口恢复到它们的最后位置。这是 WindowManager 介入的地方,并确保您的窗口每次打开时都精准放置在您想要的位置。
网友评论