Android GUI —WindowManager

2021-05-06  本文已影响0人  leap_

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会做以下几件事:

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

image.png

WindowManager

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

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

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

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

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

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

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<View> mDyingViews = new ArraySet<View>();

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

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

    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():

我们先来看看这个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;

后续详解,我们再来看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销毁的逻辑主要关注这几个:

三大流程

WMS通信

上一篇 下一篇

猜你喜欢

热点阅读