Android GUI —WindowManager

WindowManager和Window的关系可以用下面一张图来描述


Activity.attach()

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {

        mWindow = new PhoneWindow(this, window, activityConfigCallback);

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

        mWindowManager = mWindow.getWindowManager();

    }

activity在ActivityThread中构造后,会立马调用attach方法,在attach方法中,创建了一个Window对象(具体是PhoneWindow),然后为当前window添加了WindowManager;

ActivityThrad.handleResumeActivity()

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
           ViewManager wm = a.getWindowManager();
            View decor = r.window.getDecorView();
                  wm.addView(decor, l);

    }

在Activity执行onResume的时候:ActivityThread会做以下几件事:

  • 获取windowManager
  • 获取Window的root View,即DecorView
  • wm.addView(decor, l),wm执行添加window操作

wm添加的实际内容是window的根View,所以说Window只是一个概念,上图PhoneWindow虚线表示不存在,实际上是DecorView

image.png

WindowManager

WindowManager的操作也就addView,upDateView,removeView这些,这些操作都是委托给内部的单例WindowMangerGlobal来实现的,直接来看mGlobal的实现即可:

在深入了解window操作view的实现之前,需要先认识下WindowMangerGlobal的三个重要的成员变量:

    private final ArrayList mViews = new ArrayList();
    private final ArrayList mRoots = new ArrayList();
    private final ArrayList mParams = new ArrayList();

WindowMangerGlobal是一个单例,所以在一个进程中只会存在一个mGlobal对象,这三个集合维护了所有的View,ViewRootImpl,WindowParams,并且他们三一一对应;
这里的View指Window的最parent的那个View,比如DecorView,mStatusBarView

WindowManagerGlobal.addView():

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

        ViewRootImpl root;
           root = new ViewRootImpl(view.getContext(), display);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
      
                root.setView(view, wparams, panelParentView);
    }
  • 更新mGlobal中维护的三个全局缓存
  • 创建ViewRootImpl对象,将需要添加的view(decorView,window,随便叫什么吧)交给ViewRootImpl去做事情;

这里引入了一个新的概念,ViewRootImpl,这是一个比较重要的角色,具体的功能如下:

  • 执行View的三大流程
  • 管理Surface
  • 负责同WMS通信
  • 转发input事件

关于ViewRootImpl的内容后续展开,下面继续学习WM的updateView 和 removeView操作

WindowManagerGlobal.updateViewLayout()

   public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

        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);
        }
    }
  • 首先将需要更新的params设置到view对象中
  • 然后在找到这个view在那三个集合中的index(因为他们是一一对应的)
  • 拿到index 就能拿到对应的 viewRootImpl 和 old Params
  • 更新params 集合中的对应值为最新值
  • 调用ViewRootImpl的setLayoutParams()实现具体的update细节,后续在ViewRootImpl中展开

WindowManagerGlobal.removeView()

    public void removeView(View view, boolean immediate) {

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);

        }
    }

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

全局除了那三个一一对应的集合外,还有一个集合:

    private final ArraySet mDyingViews = new ArraySet();

用于暂时保存即将remove的View;在每一次addView添加window的时候都会进行判断,如果即将添加的View在mDyingViews存在,就不会添加,并且让他去死(ViewRootImpl的doDie);

  • 根据immediate字段,如果true,就调用ViewRootImpl的die(),并根据die()的回调判断是否缓存到mDyingViews 集合,当再次添加这个window的时候再销毁;
WindowManagerGlobal.addView():
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

ViewRootImpl

上文我们知道,WM的实际逻辑是交给了WMGlobal来处理的,而WMGlobal的操作Window的三个方法,都涉及到了ViewRootImpl这个类,下面我们来看看这个类的实现:

上面分析WMGlobal的时候我们遗留了四个问题:

  • ViewRootImpl.setView()
  • ViewRootImpl.setLayoutParams()
  • ViewRootImpl.die()
  • ViewRootImpl.doDie()

ViewRootImpl.setView()

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                }
    }

mWindowSession是由aidl文件生成的一个Binder对象,上面所有的操作都是在App进程进行的,到了这一步,viewRootImpl将通过自己内部的Binder(mWindowSession)将addView这个添加window的操作交给wms;


ViewRootImpl.setLayoutParams()

    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
        synchronized (this) {
            scheduleTraversals();
        }
    }

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();  // 1 barrier
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);  // 2 post
        }
    }

这里调用了scheduleTraversals():

  • 打开主线程的内存屏障,不了解内存屏障的同学可以先阅读这一篇:Handler 二期 —细节补充
  • 调用mChoreographer post 了一个Runnable

我们先来看看这个Runnable吧:

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

这段代码也很清晰,首先,关闭内存屏障,让当前的主线程继续loop,然后调用了一个非常核心的方法:performTraversals()在这个方法中,执行了我们View的三大流程,这个内存屏障的目的就是为了让这个performTraversals()立刻执行,不需要进过MessageQueue的排队;关于View三大流程的细节不在本篇展开;

mChoreographer.postCallback()在内部会调用postCallbackDelayedInternal()

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

这个方法的代码比较多,用一张图总结一下这个方法做的事情:



这个方法主要功能有两个,一个是schedule Vsync,还有一个是doFrame;

  • Vsync调用了另一个类,暂时不资到他是干什么的
  • doFrame()会去调用在ViewRootImpl中使用mChoreographer.postCallback()提交的那个Runnable任务,上面我们已经看过这个Runnable任务主要逻辑是调用了performTraversals(),于是就来到了View绘制的三大流程;

后续详解,我们再来看ViewRootImpl留下的最后一个问题:die()和doDie()

ViewRootImpl.die()

    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.
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }

        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } else {
            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }

ViewRootImpl.doDie()

 void doDie() {
        checkThread();
        synchronized (this) {
            if (mRemoved) {
                return;
            }
            mRemoved = true;
            if (mAdded) {
                dispatchDetachedFromWindow();
            }

            if (mAdded && !mFirst) {
                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);
    }

ViewRootImpl的处理window销毁的逻辑主要关注这几个:

  • dispatchDetachedFromWindow():清除当前window的view,listener等
  • mWindowSession.finishDrawing(mWindow):通知wms
  • mSurface.release():todo
  • WindowManagerGlobal.getInstance().doRemoveView(this):删除WMGlobal中对应的三个集合中的数据(ViewRootimpl,params,view)

三大流程

WMS通信

你可能感兴趣的:(Android GUI —WindowManager)