理解Window和WindowManager

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();
        }
    }

猜你喜欢

转载自blog.csdn.net/xj531/article/details/81672171