Android Window和WindowManager

Window的創(chuàng)建

window的創(chuàng)建過(guò)程與Activity緊密相關(guān),因?yàn)锳ctivity的啟動(dòng)過(guò)程是一個(gè)很復(fù)雜的過(guò)程會(huì)令做一篇文章進(jìn)行講解,這里就知道整個(gè)過(guò)程會(huì)以ActivityThread的performLaunchActivity()來(lái)完成整個(gè)啟動(dòng),performLaunchActivity方法會(huì)通過(guò)類加載器創(chuàng)建Activity實(shí)例,

java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

并調(diào)用其attach方法

activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);

在Activity中attach方法中會(huì)創(chuàng)建一個(gè)PhoneWindow對(duì)象的Window實(shí)例,并添加回調(diào)。到這里window的創(chuàng)建就算完成,window創(chuàng)建完成但是還沒(méi)有生成相應(yīng)的View。

        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);

這個(gè)View的創(chuàng)建就是我們最熟悉的setContentView,這個(gè)方法在Activity中會(huì)直接調(diào)用PhoneWindow的setContentView,我們直接看PhoneWindow. setContentView

public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

會(huì)先判斷mContentParent是否被創(chuàng)建,這個(gè)mContentParent對(duì)象可能是DecorView也可能是DecorView的一個(gè)子View,如果沒(méi)有進(jìn)入到installDecor方法

if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }

方法中會(huì)判斷mDecor是否創(chuàng)建,沒(méi)創(chuàng)建則會(huì)通過(guò)generateDecor方法進(jìn)行創(chuàng)建,到這里DecorView創(chuàng)建完成,但是這時(shí)的DecorView還只是一個(gè)空的FrameLayout,通過(guò)generateLayout方法設(shè)置mDecor的布局文件,并且加載出id為ID_ANDROID_CONTENT這個(gè)主要的內(nèi)容展示區(qū)。mContentParent= contentParent。

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

再把通過(guò)mLayoutInflater.inflate(layoutResID, mContentParent);把Activity的布局加載到mContentParent中。Activity的布局文件已經(jīng)加載進(jìn)來(lái),這時(shí)會(huì)回調(diào)Activity的onContentChanged方法通知Activity。到這里DecorView創(chuàng)建完成。
但是此時(shí)的Window并沒(méi)有與DecorView產(chǎn)生聯(lián)系,因?yàn)閃indowManager還沒(méi)有將DecorView添加到Window。在ActivityThread中的handleResumeActivity方法中,這個(gè)方法會(huì)完成DecorView添加到Window的過(guò)程。

 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
      ...
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

           ...
    }

以上關(guān)于Activity中的Window的創(chuàng)建講完了。

Window的內(nèi)部機(jī)制

通過(guò)查看Window的類,我們可以發(fā)現(xiàn)Window是一個(gè)抽象類,每一個(gè)Window都對(duì)應(yīng)著一個(gè)View和一個(gè)ViewRootImpl,Window和View通過(guò)ViewRootImpl建立聯(lián)系,因此Window并不是實(shí)際存在的,而是以View的形式存在的,這點(diǎn)可以從WindowManager看出來(lái),WindowManager繼承了ViewManager,這可以證明View時(shí)Window的存在實(shí)體,在使用中無(wú)法直接訪問(wèn)Window,必須通過(guò)WindowManager。

Window的添加過(guò)程

Window的添加過(guò)程通過(guò)WindowManager的addView實(shí)現(xiàn),實(shí)現(xiàn)WindowManager的是WindowManagerImpl類,看WindowManagerImpl.addView:

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

這個(gè)方法也并沒(méi)有直接實(shí)現(xiàn)add的操作而是交給了WindowManagerGlobal來(lái)實(shí)現(xiàn),我們來(lái)看WindowManagerGloba.addView

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

做一些參數(shù)判斷。


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

可以看到在WindowManagerGlobal中

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

這幾個(gè)變量用來(lái)存儲(chǔ)相應(yīng)的View,ViewRootImpl和WindowManager.LayoutParams,將addView的內(nèi)容添加到對(duì)應(yīng)的列表中存儲(chǔ),
這其中ViewRootImpl是在方法中通過(guò)root = new ViewRootImpl(view.getContext(), display);創(chuàng)建的,剩下的兩個(gè)則是傳參。
到這完成了view的添加。

  // 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.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

通過(guò)ViewRootImpl的setView方法返程界面更新,setView方法中通過(guò)requestLayout完成刷新,requestLayout方法中的scheduleTraversals方法

public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

scheduleTraversals方法中的mTraversalRunnable會(huì)異步執(zhí)行doTraversal,
doTraversal 方法里的performTraversals則是View繪制的開(kāi)始,View將會(huì)執(zhí)行相應(yīng)的Measure,layout,draw。

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
 void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

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

            performTraversals();

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

回到add的過(guò)程中,view的繪制是一個(gè)異步的過(guò)程,并序影響后續(xù)步驟,繼續(xù)看下邊的流程,當(dāng)完成requestLayout后,view會(huì)通過(guò)WindowSession實(shí)現(xiàn)了,mWindowSession是一個(gè)代理對(duì)象實(shí)現(xiàn)類是Session類,實(shí)現(xiàn)一次Window的添加過(guò)程一次IPC調(diào)用。

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

在Session內(nèi)部會(huì)通過(guò)WindowManagerService來(lái)實(shí)現(xiàn)addWindow

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的刪除

跟addView一樣直接看WindowManagerGlobal.removeView:

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

通過(guò)findViewLocked查找到要?jiǎng)h除View的索引,通過(guò)removeViewLocked方法刪除

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

通過(guò)ViewRootImpl來(lái)刪除View, die方法執(zhí)行這時(shí)View還沒(méi)有刪除,die中會(huì)先區(qū)分是同步還是異步,調(diào)用doDie方法,doDie中的dispatchDetachedFromWindow時(shí)會(huì)刪除View,移除相應(yīng)的回調(diào),通過(guò)mWindowSession.remove(mWindow)刪除這是一個(gè)IPC過(guò)程,調(diào)用的View的dispatchDetachedFromWindow方法,方法中會(huì)執(zhí)行

onDetachedFromWindow();
        onDetachedFromWindowInternal();

兩個(gè)方法,可以在這兩個(gè)方法中做一些資源回收。
最后執(zhí)行WindowManagerGlobal.getInstance().doRemoveView(this)刪除存儲(chǔ)的View,ViewRootImpl和WindowManager.LayoutParams。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容