1. 程式人生 > >認識Android中Window(二) 之 Window的內部機制

認識Android中Window(二) 之 Window的內部機制

簡介

我們在上一遍文章《認識Android中Window 之 懸浮窗的使用》中通過懸浮窗和Demo大概知道了Window和WindowManager的使用,今天繼續來深入探討Window的內部機制。如果看過原始碼的朋友應該知道,Window是一個抽象類,而它的實現是PhoneWindow類。WindowManager類是外界訪問Window的入口,WindowManager是一個介面,實現類在WindowManagerImpl類,真實程式碼在WindowManagerGlobal類。

Android中Window的具體實現位於系統服務WindowManagerService中,它在一個獨立的程序,所以WindowManager和WindowManagerService的互動是一個IPC過程。

理解Window 

Android中Window實質上是不存在的,View才是Window存在的實體,或者說View是通過Window來呈現,包括Toast、Dialog以及Activity。拿Activity為例說,每一個Activity都包含一個Window物件,Window對應著一個View和一個ViewRootImpl,ViewRootImpl在之前《View的工作原理》中有提過,View的繪製就是由它來完成的,Window和View通過ViewRootImpl來建立聯絡。實際上Window是一個抽象基類,它提供了一系列視窗的方法,比如我們常見的:SetContentViewfindViewById

等等,而它的唯一實現類則是PhoneWindow。它的部分原始碼大概是:

public abstract class Window {
……
@Nullable
public View findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}
public abstract void setContentView(@LayoutRes int layoutResID);
……
}

理解PhoneWindow

PhoneWindow是唯一實現Window的類,它將DecorView設為根View(DecorView實際上是一個FrameLayout

),每個Activity都有一個PhoneWindow物件。PhoneWindow是Activity和View系統互動的橋樑。正如上面所提到的SetContentView方法,其實是通過Activity中間代理了一層,最終還是會呼叫到PhoneWindow中去。正如以下原始碼:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {
private Window mWindow;
   ……
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) {
  ……
  mWindow = new PhoneWindow(this, window);
  ……
}
……
@Nullable
public View findViewById(@IdRes int id) {
    return getWindow().findViewById(id);
}
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
……
public Window getWindow() {
    return mWindow;
}
……
}

可以看到上面方法中都有使用到getWindow()這個方法,這個getWindow()方法獲取的就是Activity上的Window。再來看看PhoneWindow部分原始碼:

public class PhoneWindow extends Window implements MenuBuilder.Callback {

private DecorView mDecor;
……
@Override
public void setContentView(int layoutResID) {
    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;
}
……
}

請留意installDecor方法那一行,看得出,mContentParent為空時表示當第一次時,當前內容未放置到視窗,便呼叫了installDecor方法。installDecore方法就是用來新增DecorView根View的。大家可以自行檢視installDecore方法,這裡就略過了。在接下來通過程式碼:mLayoutInflater.inflate(layoutResID, mContentParent);的將layoutResID關聯的View新增到DecorView中去。最後還有就是通過onContentChanged方法回撥通知Activity。

理解WindowManager

WindowManager是外界訪問Window的入口,它是一個介面,它繼承於ViewManager。負責Window的管理工作,實現類是WindowManagerImpl,而新增、更新、刪除這三個對View的操作交由WindowManagerGlobal來處理。

——ViewManager

ViewManager一個介面,它是用來新增和移除activity中View的介面,它定義了一組操作View的方法:add、update、remove。

——WindowManagerImpl

WindowManagerImpl是WindowManager的實現類,實質上它沒幹什麼事情,可以理解成是一個代理,它有一個WindowManagerGlobal的單例物件,所有事情都是由WindowManagerGlobal來處理的。正如部分原始碼:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
      ……
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
    mGlobal.removeView(view, false);
}
……
}

——WindowManagerGlobal

       WindowManagerGlobal是真正處理View的新增、更新、刪除的地方。

理解WindowManagerService

WindowManagerService是一個系統服務,執行在單獨的執行緒中,管理系統中所有的Window,注意是所有。在Window的新增、更新和刪除過程中,其實就是WindowManager和WindowManagerService的IPC呼叫中完成。例如上面WindowManagerImp中addView方法,呼叫了WindowManagerGlobal的addView方法,下面來看看原始碼:

public final class 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>();
   ……
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    ……
    synchronized (mLock) {
            ……
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
    try {
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}
}

我們先來看看WindowManagerGlobal內部的幾個列表物件:

mViews               儲存的是所有 Window 所對應的 View

mRoots               儲存的是所有 Window 所對應的 ViewRootImpl

mParams            儲存的是所有 Window 所對應的佈局引數

mDyingViews     儲存了那些正在被刪除的 View 物件,或者說是那些已經呼叫了 removeView 方法但是操作刪除還未完成的 Window 物件

在addView方法中,其實就是將這些相關物件新增到上面的列表物件中,然後請看try catch中的root.setView(view, wparams, panelParentView);這行程式碼,這是呼叫了ViewRootImpl的setView方法。還記得ViewRootImpl在View的工作原理中提過嗎?View的繪製就是由它來完成的。接著我們再來看看這個setView方法到底幹了些啥事情:

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();
    ……
}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
          ……
            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();
                }
            }
            ……
            }
            ……
        }
    }
}
}

以上程式碼,重點部分還是try catch部分,請看mWindowSession.addToDisplay(…這句程式碼。這裡,mWindowSession是一個IWindowSession介面,它是一個Binder物件,真正的實現類是Session這也就是之前提到的IPC過程,然後在 Session 內部會通過 WindowManagerService的addWindow 來實現 Window 的新增。整個過程, Window 的新增請求移交給 WindowManagerService 手上去處理了 。

總結

通過上述中,對WindowViewRootImpl、PhoneWindowDecorViewWindowManagerWindowManagerImpl 、WindowManagerGlobal、ViewManager和 WindowManagerService的理解,相信大家也對Window的內部機制和Window的新增過程有了一定的瞭解。其實,更新和刪除過程也是跟新增過程類似,也就是一個IPC操作移給WindowManagerService的過程。所以這三種操作過程用流程圖表現大概是這樣: