文章目录
  1. 1. Window与WindowManager
  2. 2. Window的内部机制
  3. 3. Window的添加过程
  4. 4. Window的删除过程
  5. 5. 4、Window的更新过程

Window是一个窗口的概念,是一个抽象类,具体实现是PhoneWindow。
通过WindowManager来创建Window。
Window的具体实现位于WindowManagerService,WindowsManager和WindowMannagerService的交互是一个IPC的过程。

Window与WindowManager

(1). 使用WindowManager添加一个Window的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Button button = new Button(this);
button.setText("button");
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
0, 0, PixelFormat.TRANSPARENT);
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = 100;
params.y = 300;
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(button,params);

上面的代码将一个Button添加到屏幕坐标为(100,300)的位置.
(2).WindowManager.LayoutParams中的flag参数表示Window的属性,下面是几个比较常用的属性。

  • FLAG_NOT_FOCUSABLE

表示window不需要获取焦点,也不需要接收各种输入事件。此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的window;

  • FLAG_NOT_TOUCH_MODAL:

在此模式下,系统会将window区域外的单击事件传递给底层的window,当前window区域内的单击事件则自己处理,一般都需要开启这个标记;

  • FLAG_SHOW_WHEN_LOCKED

开启此模式可以让Window显示在锁屏的界面上

(3).TYPE参数表示Window的类型,有三种,分别是应用Window,子Window和系统Window。

应用window对应着一个Activity,子window不能独立存在,需要附属在特定的父window之上,比如Dialog就是子window。系统window是需要声明权限才能创建的window,比如Toast和系统状态栏这些都是系统window,需要声明的权限是。

(4). window是分层的,每个window都对应着z-ordered,层级大的会覆盖在层级小的上面,应用window的层级范围是1~99,子window的层级范围是1000~1999,系统window的层级范围是2000~2999。
[注意,应用window的层级范围并不是1~999哟]

(5).WindowManager继承自ViewManager,常用的只有三个方法:addView、updateView和removeView。

Window的内部机制

(1).Window是一个抽象的概念,不是实际存在的,它也是以View的形式存在。在实际使用中无法直接访问Window,只能通过WindowManager才能访问Window。每个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。

(2). Window的添加、删除和更新过程都是IPC过程,以Window的添加为例,WindowManager的实现类对于addView、updateView和removeView方法都是委托给WindowManagerGlobal类,该类保存了很多数据列表,例如所有window对应的view集合mViews、所有window对应的ViewRootImpl的集合mRoots等,之后添加操作交给了ViewRootImpl来处理,接着会通过WindowSession来完成Window的添加过程,这个过程是一个IPC调用,因为最终是通过WindowManagerService来完成window的添加的。

Window的添加过程

(1)Activity的window创建过程

1.Activity的启动过程很复杂,最终会由ActivityThread中的performLaunchActivity来完成整个启动过程,在这个方法内部会通过类加载器创建Activity的实例对象,并调用它的attach方法为其关联运行过程中所依赖的一系列上下文环境变量;

2.Activity实现了Window的Callback接口,当window接收到外界的状态变化时就会回调Activity的方法,例如onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent等;

3.Activity的Window是由PolicyManager来创建的,它的真正实现是Policy类,它会新建一个PhoneWindow对象,Activity的setContentView的实现是由PhoneWindow来实现的;

4.Activity的顶级View是DecorView,它本质上是一个FrameLayout。如果没有DecorView,那么PhoneWindow会先创建一个DecorView,然后加载具体的布局文件并将view添加到DecorView的mContentParent中,最后就是回调Activity的onContentChanged通知Activity视图已经发生了变化;

5.还有一个步骤是让WindowManager能够识别DecorView,在ActivityThread调用handleResumeActivity方法时,首先会调用Activity的onResume方法,然后会调用makeVisible方法,这个方法中DecorView真正地完成了添加和显示过程。

1
2
3
ViewManager vm = getWindowManager();
vm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;

(2)Dialog的Window创建过程

1.过程与Activity的Window创建过程类似,普通的Dialog的有一个特别之处,即它必须采用Activity的Context,如果采用Application的Context会报错。原因是Application没有应用token,应用token一般是Activity拥有的。

(3)Toast的Window创建过程

1.Toast属于系统Window,它内部的视图由两种方式指定:一种是系统默认的演示;另一种是通过setView方法来指定一个自定义的View。

2.Toast具有定时取消功能,所以系统采用了Handler。Toast的显示和隐藏是IPC过程,都需要NotificationManagerService来实现。在Toast和NMS进行IPC过程时,NMS会跨进程回调Toast中的TN类中的方法,TN类是一个Binder类,运行在Binder线程池中,所以需要通过Handler将其切换到当前发送Toast请求所在的线程,所以Toast无法在没有Looper的线程中弹出。

3.对于非系统应用来说,mToastQueue最多能同时存在50个ToastRecord,这样做是为了防止DOS(Denial of Service,拒绝服务)。因为如果某个应用弹出太多的Toast会导致其他应用没有机会弹出Toast。

Window的删除过程

(1)Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。

(2)WindowManagerGlobal.java中的removeView方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/***
* WindowManager的removeView的最终源头:
* @param view
* @param immediate
*/
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// 首先查找待删除的View的索引,查找过程就是遍历建立的数组:
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
// 然后调用这个方法进一步删除:
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}

(3)removeViewLocked是通过ViewRootImpl来完成删除操作的。在WindowManager中提供了两种删除接口removeView和removeViewImmediate,它们分别表示异步删除和同步删除,其中removeViewImmediate使用起来需要特别注意,一般来说不需要使用此方法来删除Window以免发生意外的错误。具体的删除操作由ViewRootImpl的die方法来完成。在die的内部会判断是异步删除还是同步删除。在异步删除的情况下,die方法只是发送了一个请求删除的消息后就立刻返回了,这个时候View并没有完成删除操作,所以最后会将其添加到mDyingViews中,mDyingViews表示待删除的View列表。

WindowManagerGlobal.java中的removeViewLocked方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 根据待删除View的index来做进一步删除View,
* removeViewLocked是通过ViewRootImpl来完成删除操作的。
* */
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}

(4)具体的删除操作由ViewRootImpl的die方法来完成。 在die的内部会判断是异步删除还是同步删除。在异步删除的情况下,die方法只是发送了一个请求删除的消息后就立刻返回了,这个时候View并没有完成删除操作。doDie内部会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。

ViewRootImpl.java中die方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
/*
* 如果是同步删除(立即删除),那么就不发送消息直接调用doDie方法。
* */
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
/*
* 如果是异步操作,那么就发送一个MSG_DIE的消息,
* ViewRootImpl中的Handler会处理此消息并调用doDie方法。
* */
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}

(5)ViewRootImpl.java中doDie方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
* 在Die方法中会判断是用异步删除还是同步删除,
* 但归根结底还是要用doDie来完成删除View的操作。
* 在doDie的内部会调用dispatchDetachedFromWindow方法,
* 真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。
* */
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
/*
* 这儿才是重点呢:
* */
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
invalidateDisplayLists();
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}

(5)doDie方法中调用的dispatchDetachedFromWindow是真正删除View的逻辑。

在doDie方法中调用,实现真正的删除View的逻辑。在这个方法中主要做四件事情:
(1)垃圾回收相关的工作,比如清除数据和消息、移除回调。
(2)通过Session的remove方法删除Window:mWindowSession.remove(mWindow),这同样是一个IPC过程, 最终会调用WindowManagerService的removeWindow方法。
(3)调用View的dispatchDetachedFromWindow方法:

* 在内部会调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。
* 对于onDetachedFromWindow()大家一定不陌生,当View从Window中移除时,这个方法就会被调用,
* 可以在这个方法内部做一些资源回收的工作,
* 比如终止动画、停止线程等。

(4)调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews, 需要将当前Window所关联的这三类对象从列表中删除。

ViewRootImpl.java中dispatchDetachedFromWindow方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
if (mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.validate();
}
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
mAttachInfo.mSurface = null;
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
unscheduleTraversals();
}

(7)整体的调用关系是:

ViewManager –>> WindowManager(继承自ViewManager) –>> WindowManagerImpl(继承自WindowManager) –>>WindowManagerGlobal(WindowManagerImpl内部的一个对象) –>> ViewRooImpl.die(ViewRooImpl是WindowManagerGlobal的removeView方法中的一个对象) –>> doDie(die中的一个方法调用,判断异步还是同步删除) –>> dispatchDetachedFromWindow(在doDie方法中调用,真正用于删除View的逻辑) –>> 通过Session的remove方法删除Window(IPC过程) –>> WindowManagerService.removeWindow –>> dispatchDetachedFromWindow(这个是子View的dispatchDetachedFromWindow方法) –>> onDetachedFromWindow和onDetachedFromWindowInternal(都是子View中的) –>> WindowManagerGlobal.doRemoveView

4、Window的更新过程

(1)从WindowManagerGlobal的updateViewLayout方法看起:

首先它需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImpl的setLayoutParams方法来实现的。在ViewRootImpl中会通过scheduleTraversals方法来对View重新布局,包括测量、布局、重绘这三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,它同样是一个IPC过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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的LayoutParams并替换掉老的LayoutParams,
* */
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);
}
}

(2)整体的调用关系是:

ViewManager –>> WindowManager(继承自ViewManager) –>> WindowManagerImpl(继承自WindowManager) –>>WindowManagerGlobal(WindowManagerImpl内部的一个对象) –>> updateViewLayout(WindowManagerGlobal中的一个方法) –>> setLayoutParams(updateViewLayout方法中调用) –>> ViewRootImpl.setLayoutParams(ViewRootImpl是updateViewLayout中的一个对象) –>> scheduleTraversals方法(在setLayoutParams中调用,在ViewRootImpl中) –>> WindowSession(在ViewRootImpl中,是一个Binder) –>> WindowManagerService.relayoutWindow(具体实现,更新Window的视图,IPC)


未经许可不得转载,转载请注明zilianliuxue的blog,本人保留所有版权。

文章目录
  1. 1. Window与WindowManager
  2. 2. Window的内部机制
  3. 3. Window的添加过程
  4. 4. Window的删除过程
  5. 5. 4、Window的更新过程