window表示一个窗口的概念,如果我们需要在桌面显示一个类似悬浮窗的东西,就需要用到window来实现。window是一个抽象类,它的具体实现是phoneWidow。创建一个widow只需要通过WindowManager即可,WindowManager是外界访问window的入口,window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC的过程。
1. Window 和 WindowManager
下面代码演示了通过WindowManager添加Window的过程
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mFloatingButton = new Button(this);
mFloatingButton.setText("button");
WindowManager.LayoutParams mlayoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT);
//设置flag
mlayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//设置window类型
mlayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
mlayoutParams.gravity = Gravity.CENTER;
mlayoutParams.x = 100;
mlayoutParams.y = 100;
WindowManager windowManager = getWindowManager();
windowManager.addView(mFloatingButton, mlayoutParams);
}
}
WindowManager.LayoutParams中的flag和type参数比较重要
flag参数表示window的属性;type参数表示window的类型
window的类型:
window有三种类型:应用window,子window,系统window。应用window对应着一个activity,子window不能单独存在,它需要附属在特定的父window中,比如常见的一些dialog就是一个字window。系统window需要声明权限才可以创建。
window分层的:每个window都有对应的z-ordered,层级大的会覆盖在层级小的window上面。三类window中应用window层级范围1~99,子window层级范围1000~1999,系统window是2000~2999,这些层级范围对应着WindowManager.LayoutParams中的type参数。
**WindowManager继承自ViewManager,他常用的三个方法定义在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);
}
WindowManager是一个接口,它的实现类是WindowManagerImpl
public interface WindowManager extends ViewManager {
......
}
WindowManagerImpl实现了addView、updateViewLayout、removeView三个方法,下面是截取的WindowManagerImpl 部分源码
public final class WindowManagerImpl implements WindowManager {
......
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
83 public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
84 applyDefaultToken(params);
85 mGlobal.addView(view, params, mDisplay, mParentWindow);
86 }
87
88 @Override
89 public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
90 applyDefaultToken(params);
91 mGlobal.updateViewLayout(view, params);
92 }
//异步移除 常用此方法删除view
@Override
110 public void removeView(View view) {
111 mGlobal.removeView(view, false);
112 }
113 //同步移除
114 @Override
115 public void removeViewImmediate(View view) {
116 mGlobal.removeView(view, true);
117 }
}
可以看到,WindowManagerImpl 也并没有直接实现window的三个操作,而是交给了mGlobal,从源码中我们可以发现private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); 因此mGlobal就是WindowManagerGlobal 的实例,WindowManagerImpl 将window的操作委托给WindowManagerGlobal 来实现。
2.window的内部机制
2.1window的添加
window的添加通过WindowManager的addview方法来实现,根据上面分析我们知道,最后的实现是由WindowManagerGlobal 中的addview方法来完成:
public void addView(View view, ViewGroup.LayoutParams params,
232 Display display, Window parentWindow) {
233 if (view == null) {
234 throw new IllegalArgumentException("view must not be null");
235 }
236 if (display == null) {
237 throw new IllegalArgumentException("display must not be null");
238 }
239 if (!(params instanceof WindowManager.LayoutParams)) {
240 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
241 }
242
243 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
244 if (parentWindow != null) {
245 parentWindow.adjustLayoutParamsForSubWindow(wparams);
246 } else {
247 // If there's no parent, then hardware acceleration for this view is
248 // set from the application's hardware acceleration setting.
249 final Context context = view.getContext();
250 if (context != null
251 && (context.getApplicationInfo().flags
252 & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
253 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
254 }
255 }
256
257 ViewRootImpl root;
258 View panelParentView = null;
259
260 synchronized (mLock) {
261 // Start watching for system property changes.
262 if (mSystemPropertyUpdater == null) {
263 mSystemPropertyUpdater = new Runnable() {
264 @Override public void run() {
265 synchronized (mLock) {
266 for (int i = mRoots.size() - 1; i >= 0; --i) {
267 mRoots.get(i).loadSystemProperties();
268 }
269 }
270 }
271 };
272 SystemProperties.addChangeCallback(mSystemPropertyUpdater);
273 }
274
275 int index = findViewLocked(view, false);
276 if (index >= 0) {
277 if (mDyingViews.contains(view)) {
278 // Don't wait for MSG_DIE to make it's way through root's queue.
279 mRoots.get(index).doDie();
280 } else {
281 throw new IllegalStateException("View " + view
282 + " has already been added to the window manager.");
283 }
284 // The previous removeView() had not completed executing. Now it has.
285 }
286
287 // If this is a panel window, then find the window it is being
288 // attached to for future reference.
289 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
290 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
291 final int count = mViews.size();
292 for (int i = 0; i < count; i++) {
293 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
294 panelParentView = mViews.get(i);
295 }
296 }
297 }
298
299 root = new ViewRootImpl(view.getContext(), display);
300
301 view.setLayoutParams(wparams);
302
303 mViews.add(view);
304 mRoots.add(root);
305 mParams.add(wparams);
306 }
307
308 // do this last because it fires off messages to start doing things
309 try {
310 root.setView(view, wparams, panelParentView);
311 } catch (RuntimeException e) {
312 // BadTokenException or InvalidDisplayException, clean up.
313 synchronized (mLock) {
314 final int index = findViewLocked(view, false);
315 if (index >= 0) {
316 removeViewLocked(index, true);
317 }
318 }
319 throw e;
320 }
321 }
主要分为以下几步:
- 1.检查参数是否合法,如果是子window还需要调整一些布局参数
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");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if(parentWindow != null){
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
- 2.创建ViewRootImpl并将view添加到list中
在WindowManagerGlobal 内部有几个list比较重要:
//存储所有window对应的view
private final ArrayList<View> mViews = new ArrayList<View>();
//存储所有window对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//存储所有window对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
//存储正在被删除的view对象,(已经调用了removeview方法但是删除操作还未完成)
private final ArraySet<View> mDyingViews = new ArraySet<View>();
在addview中通过下面代码中widow的一系列对象添加到list中
root = new ViewRootImpl(view.getContext(),display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
- 3.通过ViewRootImpl来更新界面并完成widow的添加
// do this last because it fires off messages to start doing things
309 try {
//通过此方法更新界面
310 root.setView(view, wparams, panelParentView);
311 } catch (RuntimeException e) {
312 // BadTokenException or InvalidDisplayException, clean up.
313 synchronized (mLock) {
314 final int index = findViewLocked(view, false);
315 if (index >= 0) {
316 removeViewLocked(index, true);
317 }
318 }
319 throw e;
320 }
可以看到调用ViewRootImpl的setview方法来完成这个步骤,在setview方法内部会通过requestLayout来完成异步刷新请求。截取部分代码如下
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
448 synchronized (this) {
449 if (mView == null) {
450 mView = view;
451
452 mAttachInfo.mDisplayState = mDisplay.getState();
453 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
454
455 mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
456 mFallbackEventHandler.setView(view);
457 mWindowAttributes.copyFrom(attrs);
458 if (mWindowAttributes.packageName == null) {
459 mWindowAttributes.packageName = mBasePackageName;
460 }
461 attrs = mWindowAttributes;
462 // Keep track of the actual window flags supplied by the client.
463 mClientWindowLayoutFlags = attrs.flags;
464
465 setAccessibilityFocus(null, null);
466
467 if (view instanceof RootViewSurfaceTaker) {
468 mSurfaceHolderCallback =
469 ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
470 if (mSurfaceHolderCallback != null) {
471 mSurfaceHolder = new TakenSurfaceHolder();
472 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
473 }
474 }
475
476 //......省略了部分代码
505 // Schedule the first layout -before- adding to the window
520 // manager, to make sure we do the relayout before receiving
521 // any other events from the system.
requestLayout();//调用requestLayout方法异步刷新
523 if ((mWindowAttributes.inputFeatures
524 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
525 mInputChannel = new InputChannel();
526 }
527 try {
528 mOrigWindowType = mWindowAttributes.type;
529 mAttachInfo.mRecomputeGlobalAttributes = true;
530 collectViewAttributes();
531 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
532 getHostVisibility(), mDisplay.getDisplayId(),
533 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
534 mAttachInfo.mOutsets, mInputChannel);
535 } catch (RemoteException e) {
536 mAdded = false;
537 mView = null;
538 mAttachInfo.mRootView = null;
539 mInputChannel = null;
540 mFallbackEventHandler.setView(null);
541 unscheduleTraversals();
542 setAccessibilityFocus(null, null);
543 throw new RuntimeException("Adding window failed", e);
544 } finally {
545 if (restore) {
546 attrs.restore();
547 }
548 }
549
550 }
requestLayout方法如下:
public void requestLayout(){
if(!mHandingLayoutInLayoutRequest){
checkThread();
mLayoutRequested = true;
scheduleTraversals();//view的绘制入口
}
}
可以看到scheduleTraversals方法实际是view的绘制入口
接着继续看setView方法,在requestLayout方法之后,接着会通过mWindowSession 来完成window的添加,如下:
try {
528 mOrigWindowType = mWindowAttributes.type;
529 mAttachInfo.mRecomputeGlobalAttributes = true;
530 collectViewAttributes();
//添加widow
531 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
532 getHostVisibility(), mDisplay.getDisplayId(),
533 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
534 mAttachInfo.mOutsets, mInputChannel);
535 } catch (RemoteException e) {
536 mAdded = false;
537 mView = null;
538 mAttachInfo.mRootView = null;
539 mInputChannel = null;
540 mFallbackEventHandler.setView(null);
541 unscheduleTraversals();
542 setAccessibilityFocus(null, null);
543 throw new RuntimeException("Adding window failed", e);
544 } finally {
545 if (restore) {
546 attrs.restore();
547 }
548 }
在上面代码中,mWindowSession是IWindowSession类型,是一个Binder对象,真正的实现类是Session,也就是说window的添加过程是一次IPC调用,在Session内部的addToDisplay方法中最终会通过WindowManagerService 的addwindow方法来实现window的添加,代码如下:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams, attrs, int viewVisibility,
int displayId, Rect outContentInsets, InputChannel outInputChannel){
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel);
}
2.2window 的删除
删除过程和添加类似,还是通过WindowManagerGlobal 实现,下面试WindowManagerGlobal 的removeview方法
public void removeView(View view, boolean immediate) {
345 if (view == null) {
346 throw new IllegalArgumentException("view must not be null");
347 }
348
349 synchronized (mLock) {
350 int index = findViewLocked(view, true);
351 View curView = mRoots.get(index).getView();
352 removeViewLocked(index, immediate);
353 if (curView == view) {
354 return;
355 }
356
357 throw new IllegalStateException("Calling with view " + view
358 + " but the ViewAncestor is attached to " + curView);
359 }
360 }
删除过程首先通过findViewLocked方法来查找待删除的view的索引,然后再调用removeViewLocked来做进一步删除,如下:
private void removeViewLocked(int index, boolean immediate) {
388 ViewRootImpl root = mRoots.get(index);
389 View view = root.getView();
390
391 if (view != null) {
392 InputMethodManager imm = InputMethodManager.getInstance();
393 if (imm != null) {
394 imm.windowDismissed(mViews.get(index).getWindowToken());
395 }
396 }
//注意die方法
397 boolean deferred = root.die(immediate);
398 if (view != null) {
399 view.assignParent(null);
400 if (deferred) {
401 mDyingViews.add(view);
402 }
403 }
404 }
由root.die(immediate);可以看到,removeViewLocked方法是用过ViewRootImpl 来完成删除操作的,die方法只是发送了一个请求删除的消息,这个时候view还没完成删除操作,所以方法最后的代码会将其添加到mDyingViews中,表示待删除,ViewRootImpl 的die方法如下:
boolean die(boolean immediate) {
5580 // Make sure we do execute immediately if we are in the middle of a traversal or the damage
5581 // done by dispatchDetachedFromWindow will cause havoc on return.
5582 if (immediate && !mIsInTraversal) {
5583 doDie();
5584 return false;
5585 }
5586
5587 if (!mIsDrawing) {
5588 destroyHardwareRenderer();
5589 } else {
5590 Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
5591 " window=" + this + ", title=" + mWindowAttributes.getTitle());
5592 }
5593 mHandler.sendEmptyMessage(MSG_DIE);
5594 return true;
5595 }
5596
die方法内部做了简单的判断,如果是异步删除,那么久发送一个MSG_DIE的消息,ViewRootImpl 中的Handler会处理此消息并调用doDie方法,如果是同步删除,就不发送消息,直接调用doDie方法。handler处理消息代码如下:
@Override
3256 public void handleMessage(Message msg) {
3257 switch (msg.what) {
3258 case MSG_INVALIDATE:
3259 ((View) msg.obj).invalidate();
3260 break;
//......省略代码
3408 case MSG_DIE:
3409 doDie();
3410 break;
///......省略代码
3489 }
doDie方法内部会调用dispatchDetachedFromWindow方法,真正删除view的逻辑在该方法内部实现,在dodie方法最后会调用 WindowManagerGlobal.getInstance().doRemoveView(this);方法来刷新数据,将当前window关联的对象从mRoots mParams mDyingViews list中删除
doDie方法如下
void doDie() {
5598 checkThread();
5599 if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
5600 synchronized (this) {
5601 if (mRemoved) {
5602 return;
5603 }
5604 mRemoved = true;
5605 if (mAdded) {
//删除view的逻辑
5606 dispatchDetachedFromWindow();
5607 }
5608
5609 if (mAdded && !mFirst) {
5610 destroyHardwareRenderer();
5611
5612 if (mView != null) {
5613 int viewVisibility = mView.getVisibility();
5614 boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
5615 if (mWindowAttributesChanged || viewVisibilityChanged) {
5616 // If layout params have been changed, first give them
5617 // to the window manager to make sure it has the correct
5618 // animation info.
5619 try {
5620 if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
5621 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
5622 mWindowSession.finishDrawing(mWindow);
5623 }
5624 } catch (RemoteException e) {
5625 }
5626 }
5627
5628 mSurface.release();
5629 }
5630 }
5631
5632 mAdded = false;
5633 }
5634 WindowManagerGlobal.getInstance().doRemoveView(this);
5635 }
5636
dispatchDetachedFromWindow方法主要做一下几件事:
1.垃圾回收相关工作,比如清楚数据和消息,移除毁掉
2.通过mWindowSession.remove(mWindow)方法来删除window,这同样是一个IPC过程,最终会调用windowmanagerservice中的removewindow方法
3.调用view的dispatchDetachedFromWindow方法时会在内部调用view的onDetachedFronWindow方法
void dispatchDetachedFromWindow() {
3066 if (mView != null && mView.mAttachInfo != null) {
3067 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
3068 mView.dispatchDetachedFromWindow();
3069 }
3070
3071 mAccessibilityInteractionConnectionManager.ensureNoConnection();
3072 mAccessibilityManager.removeAccessibilityStateChangeListener(
3073 mAccessibilityInteractionConnectionManager);
3074 mAccessibilityManager.removeHighTextContrastStateChangeListener(
3075 mHighContrastTextManager);
3076 removeSendWindowContentChangedCallback();
3077
3078 destroyHardwareRenderer();
3079
3080 setAccessibilityFocus(null, null);
3081
3082 mView.assignParent(null);
3083 mView = null;
3084 mAttachInfo.mRootView = null;
3085
3086 mSurface.release();
3087
3088 if (mInputQueueCallback != null && mInputQueue != null) {
3089 mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
3090 mInputQueue.dispose();
3091 mInputQueueCallback = null;
3092 mInputQueue = null;
3093 }
3094 if (mInputEventReceiver != null) {
3095 mInputEventReceiver.dispose();
3096 mInputEventReceiver = null;
3097 }
3098 try {
3099 mWindowSession.remove(mWindow);
3100 } catch (RemoteException e) {
3101 }
3102
3103 // Dispose the input channel after removing the window so the Window Manager
3104 // doesn't interpret the input channel being closed as an abnormal termination.
3105 if (mInputChannel != null) {
3106 mInputChannel.dispose();
3107 mInputChannel = null;
3108 }
3109
3110 mDisplayManager.unregisterDisplayListener(mDisplayListener);
3111
3112 unscheduleTraversals();
3113 }
3114
2.3 window的更新
window的更新过程还是从WindowManagerGlobal 开始,调用updateViewLayout方法,如下:
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
324 if (view == null) {
325 throw new IllegalArgumentException("view must not be null");
326 }
327 if (!(params instanceof WindowManager.LayoutParams)) {
328 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
329 }
330
331 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
332
333 view.setLayoutParams(wparams);
334
335 synchronized (mLock) {
336 int index = findViewLocked(view, true);
337 ViewRootImpl root = mRoots.get(index);
338 mParams.remove(index);
339 mParams.add(index, wparams);
340 root.setLayoutParams(wparams, false);
341 }
342 }
该方法首先通过view.setLayoutParams(wparams);来更新view的LayoutParams,然后通过root.setLayoutParams(wparams, false);更新ViewRootImpl 的LayoutParams。root.setLayoutParams(wparams, false);方法内部回通过scheduleTraversals方法来对view重新布局,包括测量,布局,重绘这三个过程,如下:
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
final int oldInsetLeft = mWindowAttributes.surfaceInsets.left;
final int oldInsetTop = mWindowAttributes.surfaceInsets.top;
final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
final int oldSoftInputMode = mWindowAttributes.softInputMode;
final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
//......
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
//......
mWindowAttributesChanged = true;
//view重绘
scheduleTraversals();
}
}
在scheduleTraversals方法中调用pokeDrawLockIfNeeded通过IPC调用WMS来更新,如下
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 最终调用了 doTraversal 来执行 测量、布局、重绘操作
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
// 这里最终通过 IPC 机制调用 wms 来更新视图
pokeDrawLockIfNeeded();
}
}