前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Window 机制源码分析

Window 机制源码分析

作者头像
Yif
发布2019-12-26 15:28:58
5740
发布2019-12-26 15:28:58
举报
文章被收录于专栏:Android 进阶
undefined
undefined

Window 机制深入源码分析

Window 是一个抽象的基类,表示一个窗口,包含一个View treelayout参数。

View treeroot View可以通过getDecorView得到。还可以设置Window的Content View。其实现类是PhoneWindowActivity,Dialog,Toast,都包含一个Window,该Window在Activity的attach()函数中mWindow = new PhoneWindow(this);创建。 每一个Window对应一个View与ViewRootImpl,它与view是通过ViewRootImpl来建立关系的,它是以view的形式存在。在实际中无法直接访问,需要通过WindowManager这个应用程序窗口管理器进行访问。而WindowManager是一个接口,继承自ViewManager,真正实现的方法是在WindowManagerImpl中。

Window 添加过程

WindowManagerImpl内部调用了WindowManagerGlobal的addView方法,采用了懒汉式线程安全的方法进行初始化WindowManagerGlobal。addView,updateView,removeView全部都是通过WindowManagerGlobal进行操作实现的。 WindowManagerGlobal的addView 方法

代码语言:javascript
复制
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        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);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
 
        ViewRootImpl root;
        View panelParentView = null;
 
        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
 
            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.
            }
 
            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
 
            root = new ViewRootImpl(view.getContext(), display);
 
            view.setLayoutParams(wparams);
 
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
 
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
  • 首先进行参数的检查,参数不对进行抛出异常 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");     }
  • 如果当前父view不为空进行一些布局参数设置,否则父view为空时,必须在应用或Activity中设置硬件加速。 final Context context = view.getContext();         if (context != null                 && (context.getApplicationInfo().flags                         & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {             wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;         }
  • 创建ViewRootImpl,并将view添加到集合中 root = new ViewRootImpl(view.getContext(), display);           view.setLayoutParams(wparams);           mViews.add(view);         mRoots.add(root);         mParams.add(wparams); 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>(); private final ArraySet<View> mDyingViews = new ArraySet<View>(); mView集合中存储着Window所对应的view,而mRoots存储着Window所对应的ViewRootImplmParams存储着Window所对应的布局参数,mDyingViews存储着Window所对应的正在被删除的view。
  • 调用ViewRootImpl来设置view,并完成view的添加过程 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {     synchronized (this) {         if (mView == null) {             mView = view;               mAttachInfo.mDisplayState = mDisplay.getState();             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);               mViewLayoutDirectionInitial = mView.getRawLayoutDirection();             mFallbackEventHandler.setView(view);             mWindowAttributes.copyFrom(attrs);             if (mWindowAttributes.packageName == null) {                 mWindowAttributes.packageName = mBasePackageName;             }        requestLayout();                 switch (res) {                     case WindowManagerGlobal.ADD_BAD_APP_TOKEN:                     case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:                         throw new WindowManager.BadTokenException(                                 "Unable to add window -- token " + attrs.token                                 + " is not valid; is your activity running?");                     case WindowManagerGlobal.ADD_NOT_APP_TOKEN:                         throw new WindowManager.BadTokenException(                                 "Unable to add window -- token " + attrs.token                                 + " is not for an application");                     case WindowManagerGlobal.ADD_APP_EXITING:                         throw new WindowManager.BadTokenException(                                 "Unable to add window -- app for token " + attrs.token                                 + " is exiting");                     case WindowManagerGlobal.ADD_DUPLICATE_ADD:                         throw new WindowManager.BadTokenException(                                 "Unable to add window -- window " + mWindow                                 + " has already been added");       ...... } 内部调用requestLayout完成整个View的绘制,同时进行当前Window检查,可不可以添加当前view @Override public void requestLayout() {     if (!mHandlingLayoutInLayoutRequest) {         checkThread();         mLayoutRequested = true;         scheduleTraversals();     } } scheduleTraversals内部最终会调用performTraversals来完成整个view的绘制
  • 最后通过WindowSession来完成Window的添加过程 IWindowSession mWindowSession是一个binder接口对象,添加一个也就完成一个的Binder进程通信 try {                 mOrigWindowType = mWindowAttributes.type;                 mAttachInfo.mRecomputeGlobalAttributes = true;                 collectViewAttributes();                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                         getHostVisibility(), mDisplay.getDisplayId(),                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                         mAttachInfo.mOutsets, mInputChannel);             } catch (RemoteException e) {                 mAdded = false;                 mView = null;                 mAttachInfo.mRootView = null;                 mInputChannel = null;                 mFallbackEventHandler.setView(null);                 unscheduleTraversals();                 setAccessibilityFocus(null, null);                 throw new RuntimeException("Adding window failed", e);             } finally {                 if (restore) {                     attrs.restore();                 }             } addToDisplay方法,内部调用WindowManagerServiceaddWindow方法完成Window的添加过程,它会为每一个应用保留一个单独的Sessions对象。Window的添加过程最后统统交给了WindowManagerServiceaddWindow方法来完成。 @Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,         int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,         Rect outOutsets, InputChannel outInputChannel) {     return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,             outContentInsets, outStableInsets, outOutsets, outInputChannel); }

Window更新过程

Window更新过程照样调用的是WindowManagerGlobalupdateViewLayout方法。

代码语言:javascript
复制
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.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);
        }
    }

首先获取需要更新的当前view,移除当前view位置的参数,并将新的布局参数添加进去,通过新的LayoutParams来替换旧的layoutParams

Window 删除过程

Window删除过程调用的是WindowManagerGlobalremoveView方法。

代码语言:javascript
复制
 public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
 
        synchronized (mLock) {
            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);
        }
    }

首先查找需要删除view的位置,再调用removeViewLocked进行删除,删除分为两种,一种是异步删除,一种是同步删除。mGlobal.removeView(view, false);,false为异步删除,true为同步删除mGlobal.removeView(view, true);异步删除主要是通过发送一个handler消息,然后调用die方法进行删除,这个删除不是立刻就删除了,而是添加到队列中,mDyingViews表示待删除的view列表。

代码语言:javascript
复制
/**
     * @param immediate True, do now if not in traversal. False, put on queue and do later.
     * @return True, request has been queued. False, request has been completed.
     */
    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;
    }

die方法内部做了一个判断,是否立即删除,立即删除(同步删除)就不用发消息,直接调用doDie方法,而异步删除需要发送一个消息。

代码语言:javascript
复制
void doDie() {
        checkThread();
        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
        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);
    }

真正的删除操作发生在dispatchDetachedFromWindow方法中

代码语言:javascript
复制
 void dispatchDetachedFromWindow() {
        if (mView != null && mView.mAttachInfo != null) {
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
            mView.dispatchDetachedFromWindow();
        }
 
        mAccessibilityInteractionConnectionManager.ensureNoConnection();
        mAccessibilityManager.removeAccessibilityStateChangeListener(
                mAccessibilityInteractionConnectionManager);
        mAccessibilityManager.removeHighTextContrastStateChangeListener(
                mHighContrastTextManager);
        removeSendWindowContentChangedCallback();
 
        destroyHardwareRenderer();
 
        setAccessibilityFocus(null, null);
 
        mView.assignParent(null);
        mView = null;
        mAttachInfo.mRootView = 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;
        }
 
        mDisplayManager.unregisterDisplayListener(mDisplayListener);
 
        unscheduleTraversals();
    }
  • 首先进行垃圾回收操作,清除数据与移除消息和监听;
  • 通过mWindowSession.remove(mWindow);,它是一次IPC进程操作,与addView一样,最后会调用WindowManagerremoveWindow方法;
  • ViewdispatchDetachedFromWindow方法,内部会调用onDetachedFromWindow方法,方法中可以进行资源回收,比如停止动画,与线程操作;
  • 最后调用WindowManagerGlobaldoRemoveView将之前的mRoots,mParams,mDyingView中的Window关联的ViewRootImpl对象移除 void doRemoveView(ViewRootImpl root) {     synchronized (mLock) {         final int index = mRoots.indexOf(root);         if (index >= 0) {             mRoots.remove(index);             mParams.remove(index);             final View view = mViews.remove(index);             mDyingViews.remove(view);         }     }     if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {         doTrimForeground();     } }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年9月18日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Window 机制深入源码分析
    • Window 添加过程
      • Window更新过程
        • Window 删除过程
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档