1. 程式人生 > >android中window和windowManager原始碼分析(android-api-23)

android中window和windowManager原始碼分析(android-api-23)

一、前言

在android中window無處不在,如activity、dialog、toast等。它是view所依附的載體,每個window都對應於一個View和一個ViewRootImpl。ViewRootImpl就是Window和view的連線紐帶。windowManager顧名思義是實現對window的管理,事實上,是實現對依附於window中的view的管理。

二、從dialog開始

dialog是大家都很熟悉的東西,在實際的專案開發中幾乎都能用到。因此很有必要了解下dialog的內部實現,以明白其實現原理。對於dialog的使用最簡單的形式,就是下列這種形式:

new Dialog(MainActivity.this).show();
只需要上面簡單的一句程式碼,即可出show出來一個對話方塊來,根據前言可知,這裡顯然涉及到了window的概念。因此,這裡先分兩個方向走,一個是dialog的構造方法,一個是show方法,來探究window的概念。

2.1 dialog的構造方法

dialog有多個構造方法,但最終都會呼叫下面的構造方法,如下所示。

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    if (createContextThemeWrapper) {
        if (themeResId == 0) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }
    //這裡出現了我們將要探討的windowManager,事實上無論是activity還是dialog都會持有windManager服務
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    //這裡第一次出現了我們要探討了window,即產生了一個實際的PhoneWindow物件,事實上,Window僅僅是個抽象類,
    //對於手機,一般是由PhoneWindow進行實現
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    //從下面可以看出來,dialog有自己處理事件的能力,因為window在此設定了callback
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}
上面的程式碼比較簡單,核心是產生了一個WindowManager和Window物件,並設定了一些回撥方法,以便進行window事件的相關處理。我們都知道dialog是依附於activity而存在的,但能獨立處理一些事件,上面的程式碼給出了答案。其實從dialog的構造方法來看顯然沒有做什麼有效的工作,so,真正的主題應該是在show方法中了。

2.2 dialog的show方法分析

public void show() {
//這裡處理了已經show了的情況,這裡無需討論
    if (mShowing) {        
        if (mDecor != null) {            
            if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {                
                mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);            
                
            }            
            mDecor.setVisibility(View.VISIBLE);        
        }        
        return;   
    }    
    mCanceled = false;    
//這裡如果沒有create則會呼叫dispatchOncreate,通過該方法,最終會呼叫實際的dialog中的onCreate方法
//之所以說是實際的,因為我們可能會繼承dialog進而複寫onCreate方法
    if (!mCreated) {
        dispatchOnCreate(null);
    }

//dialog所有的初始化工作建議在onstart去做
    onStart();
//這裡,引入了decorView,這個是所有檢視的底層佈局,型別為FrameLayout
    mDecor = mWindow.getDecorView();

    if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
        final ApplicationInfo info = mContext.getApplicationInfo();
        mWindow.setDefaultIcon(info.icon);
        mWindow.setDefaultLogo(info.logo);
        mActionBar = new WindowDecorActionBar(this);
    }

    WindowManager.LayoutParams l = mWindow.getAttributes();
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
        nl.copyFrom(l);
        nl.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        l = nl;
    }

    try {
//windowManager將decorView加入了window視窗,這裡真正涉及到windowManager對window的管理了(實際上是對view進行管理)
        mWindowManager.addView(mDecor, l);
        mShowing = true;

//這裡真正的去show了,不在討論的範圍了。
        sendShowMessage();
    } finally {
    }
}
上面程式碼清晰了展現出了dialog show的過程。主要關注幾個點:dispathOnCreate、mWindow.getDecorView()以及
mWindowManager.addView(mDecor, l);在本章節我們只簡單分析dispathOnCreate。這樣也會對mDecor的由來有個認知。至於mWindowManager.addView(mDecor, l)分析將會真正引出本文的主題。
2.3 dispatOnCreate
在上一小節中,dialog show的過程中,呼叫了dispatOnCreate方法,改方法如下:
void dispatchOnCreate(Bundle savedInstanceState) {
    if (!mCreated) {
        onCreate(savedInstanceState);
        mCreated = true;
    }
}
即呼叫了onCreate方法,檢視dialog的onCreate方法可知,該方法是個空方法,但是建議在該方法中初始化檢視相關如,呼叫setContentView:
/**
 * Similar to {@link Activity#onCreate}, you should initialize your dialog
 * in this method, including calling {@link #setContentView}.
 * @param savedInstanceState If this dialog is being reinitalized after a
 *     the hosting activity was previously shut down, holds the result from
 *     the most recent call to {@link #onSaveInstanceState}, or null if this
 *     is the first time.
 */
protected void onCreate(Bundle savedInstanceState) {
}
因此,這裡可以參考子類的實現,子類的實現方式可參考下面程式碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
//設定檢視
    setContentView(mView);
//初始化dialog的一些資料
    initDialog();
}
因此這裡主要分析setContentView,這個實際上是dialog內部方法,實現為:
/**
 * Set the screen content to an explicit view.  This view is placed
 * directly into the screen's view hierarchy.  It can itself be a complex
 * view hierarchy.
 * 
 * @param view The desired content to display.
 */
public void setContentView(View view) {
//這裡顯然呼叫了mWindow進行檢視設定,這裡的mWindow實際上就是構造方法中的PhoneWindow物件。
//最終將contentView依附於window之上,但這個是使用者自定義內容的檢視。
    mWindow.setContentView(view);
}
//mWindow實際上PhoneWindow物件。因此setContentView的實現就是PhoneWindow中的setContentView實現
//setContentView最終會呼叫下面的程式碼
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    // 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) {
//這裡就是生成decorView,dialog show方法中的mWindow.getDecor方法獲取的實際上就是在此時生成的。
//decorview實際上是android中最頂層的一個佈局,其繼承於FrameLayout,是所有view的viewGroup,所有的view都將包括於該ViewGroup中,
//包括剛剛我們setContentView中的展示的自定義view
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
//從這裡可以看出,我們剛剛設定的contentView會被設定到content區域,這個mContentParent實際上是通過下面程式碼獲取的
//ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);從程式碼可以看出,這裡獲取了佈局中的
//內容區域
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}
至此,我們瞭解到了整個view的生成過程,接下來就是真正引入主題,即將view新增至window中,並交給windowManager進行管理。
三、我們的主角window和windowManager
3.1 從addView說起
在對dialog實現原始碼進行分析的時候,在show方法的最後重要的一步就是執行了mWindowManager.addView(mDecor, l);方法,so,就從這裡開始。經過對addView的追溯發現,其實際上是ViewManager中定義的方法,定義如下所示:
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * 

Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }

顧名思義,從上述程式碼中可以看出,該介面實際上提供了對view的增、刪、改的工作,那麼這為什麼和window的管理連線到一起呢?實際上WindowManager繼承了該介面,檢視WindowManager的實現程式碼可知:
public interface WindowManagerextendsViewManager
而Dialog在構造方法中獲取的mWindow型別正是WindowManager,所以自然可以擁有對view的增、刪、改的功能,同時也說明了,windowManager本質上是對View進行管理。但是WindowManager顯然依然是個介面,其具體實現在哪兒?其實是在WindowManagerImpl中,進入其addView方法就可以明白相關實現。WindowManagerImpl中的addView方法:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
//顯然,這裡全部委託給了mGlobal來進行實現
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}
該方法的實現很簡單,重要的mGlobal的實現,跟蹤發現mGlobal是一個WindowManagerGlobal型別的物件,其addView的實現方法為:
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    
//這裡省略一些檢查,如view是否為null等....
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//1、這裡有個parentWindow判斷,為什麼?
    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);
                }
            }
        }

//2、這裡產生了ViewRootImpl型別的root物件,我們終於見到前言中提到的這個角色了
//這個物件很重要,前言中說這個是view和window中的連線紐帶,那麼ViewRootImpl具體是幹什麼的?
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);
//3、這裡出現了多個add呼叫,那麼這些層意又是什麼?
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    // do this last because it fires off messages to start doing things
    try {
//顯然,這裡將傳入的view通過root的setView完成了設定,上面提到root的型別是ViewRootImpl,
//因此很有必要探究下ViewRootImpl的setView實現。在這裡真正完成了檢視的展現。
        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;
    }
}
重要的程式碼註釋我都已經標明,首先分析下注釋中的幾個問題:
(1)window實際上有三種不同的型別,分別是系統window、應用window以及子window,而且不同型別的window有層級的概念,類似於html中的z-index,因此我們能看到系統視窗可以覆蓋到應用window之上等。Toast就是系統window,activity(activity是基視窗,其它所有的應用視窗都依賴於改視窗)、dialog就是普通的應用window(對於dialog而言子類不那麼絕對,因為很多子類都可以修改其window型別),而popupWindow則是子window。
(2)ViewRootImpl這個是Window和view之間的紐帶,這裡暫不分析,下節分析
(3)對於這些容器可參見WindowManagerGlobal中的定義
//儲存的是所有Window對應的View
private final ArrayList mViews = new ArrayList();
//儲存的是所有Window所對應的ViewRootImpl
private final ArrayList mRoots = new ArrayList();
//儲存所有window對應的佈局引數
private final ArrayList mParams =
        new ArrayList();
//儲存所有被removeView方法中刪除的view
private final ArraySet mDyingViews = new ArraySet();
ViewRootImpl中的setView實現ViewRootImpl的setView方法,比較長,這裡貼上一些必要的程式碼:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
//省略部分程式碼....
requestLayout();
//省略部分程式碼....
//這裡呼叫了mWindowSession的addToDisplay方法,完成真正的window新增
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(),
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
        mAttachInfo.mOutsets, mInputChannel);
//省略部分程式碼....
}}}
這裡但最後會呼叫一個方法:requestLayout();該方法真正完成檢視的非同步重新整理請求,起程式碼如下:
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
由此可知具體實現在scheduleTraversals,這裡就是view的繪製入口,此處不再深入,主要看window的新增。從上面程式碼中可以看出window的新增是有mWindowSession來完成的,mWindowSession型別是IWindowSession,它是個Binder物件,真正的實現類是Session,Binder大家應該熟悉,是android提供的一種ipc通訊機制,因此,window的新增過程實際上是一次ipc的呼叫。Sessin中的addToDisplay程式碼如下:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
//這裡呼叫了mService的addWindow方法,mService的型別是WindowManagerService,因此具體實現就再這個類中
//這裡會根據傳入的引數生成windstate,並對一些token進行處理,至此整個window完成了新增
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
至此,我們以Dialog為引子,完成的看到了整個window的新增流程。從這個流程可以看出,dialog(當然也包括其它檢視)中的檢視首先會以DecorView為根佈局而存在,而decorView 依附於window上。在android中window的增、刪、改,實際上都是基於view進行操作的,這也說明window是個相對虛擬的概念。window的新增完成了,或許會有很多人想知道window是怎麼樣被remove的呢?
3.2 從removeViewImmediate結束
或許讀者會有疑問,為什麼從removeViewImmediate結束?且等我慢慢道來。我們依然從dialog入手。在上章節中,我們知道dialog的show過程實際上就是window的新增過程,從dialog的show開始,到dialog消失之前顯然沒有任何機會看到window的remove過程,因為window顯然一直存在。所以,所有的神祕應該隱藏dialog的dismiss方法中。dialog的dismiss方法中基本沒有做什麼事情,就是呼叫了dismissDialog,故直接看dismissDialog的程式碼:
void dismissDialog() {
    if (mDecor == null || !mShowing) {
        return;
    }

    if (mWindow.isDestroyed()) {
        Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
        return;
    }

    try {
//重點在這裡,可以看到整個dismiss的過程就這裡在remove了。
        mWindowManager.removeViewImmediate(mDecor);
    } finally {
        if (mActionMode != null) {
            mActionMode.finish();
        }
        mDecor = null;
        mWindow.closeAllPanels();
        onStop();
        mShowing = false;

        sendDismissMessage();
    }
}
程式碼中的mWindowManager我們已經很熟悉了,就是針對window進行管理的一個介面,其具體實現在WindowManagerImpl中:
public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
}
不出所料,這裡依然交給了mGlobal進行view管理,mGlobal是WindowManagerGlobal物件,其removeView實現如下:
public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
//待remove view的索引
        int index = findViewLocked(view, true);
//mRoots前文中介紹過,儲存著每一個viewRootImpl物件
        View curView = mRoots.get(index).getView();
//重點是這裡,真正對view進行了remove操作
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }

        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}
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());
        }
    }
//重點在ViewRootImpl中的die方法中
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
//前文提到過,這個暫存remove過的view,就在這裡,因為remove需要一個過程
//那麼這裡最後在哪兒刪除?下文將有交代
            mDyingViews.add(view);
        }
    }
}

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(TAG, "Attempting to destroy the window while drawing!\n" +
                "  window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}
為便於分析這裡假設立即刪除(即同步刪除,實際上多數情況下都是非同步刪除,即發個MGS_DIE的訊息即可返回了),直接看ViewRootImpl中的doDie()方法:
void doDie() {
    checkThread();
    if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
    synchronized (this) {
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        if (mAdded) {
//這裡是移除工作的重點,下文將進行分析
            dispatchDetachedFromWindow();
        }

        if (mAdded && !mFirst) {
//硬體渲染destroy
            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;
    }
//這裡將解釋上文中view到底什麼時候被徹底remove
    WindowManagerGlobal.getInstance().doRemoveView(this);
}
在解析dispatchDetachedFromWindow之前,先看下doRemoveView方法,該方法在WindowManagerGlobal中,具體實現如下:
void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
//以下語句,正式完成了待刪除的view的remove工作
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
        doTrimForeground();
    }
}
最後看remove過程的重點,ViewRootImpl中的dispatchDetachedFromWindow實現如下:
void dispatchDetachedFromWindow() {
    if (mView != null && mView.mAttachInfo != null) {
        mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
//將檢視從window中移除,此方法會回撥onDetachedFromWindow方法,從該方法的說明來看,
//這個時候已經沒有用於繪製的surface了,因此這裡可以做些資源回收工作
        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 {
//這裡是重點,很顯然這裡真正的完成了我們window的remove工作,下面會分析。
       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方法,mWindowSession在前文已經提到過。在view的新增addView的過程中,最終還是通過mWindowSession的addToDisplay方法來完成的,在這個方法中,addToDisplay呼叫了WindowManagerService中的addWindow方法完成了window的新增工作,這個過程是一個ipc的過程。同樣的,mWindowSession.remove方法最後依然會呼叫WindowManagerService中的removeWindow方法,實現如下:
public void removeWindow(Session session, IWindow client) {
    synchronized(mWindowMap) {
//這裡和addWindow時相對應,windowstate正是在那個時候生成的。
        WindowState win = windowForClientLocked(session, client, false);
        if (win == null) {
            return;
        }
//根據windowstate對window進行真正的移除操作
        removeWindowLocked(win);
    }
}
至此,window的remove正式完成。
四、總結
本文以dialog的展示和消失為切入點,分析了android中window和windowManager的實現機制,這個過程的細節一定是比較複雜的,但是我們只關心這條主線,從而對window的新增和刪除過程有個清晰的過程。基於dialog的分析實現,同樣適用於activity、toast中window的實現,雖有不同,但大同小異。

最後,本文的文字純手動打字,如若出現錯別字還請諒解。