Android解析WindowManager(一)WindowManager體系

劉望舒發表於2017-09-11

相關文章
Android系統啟動系列
Android深入四大元件

前言

WindowManagerService(WMS)和AMS一樣,都是Android開發需要掌握的知識點,同樣的,WMS也很複雜,需要多篇文章來進行講解,為何更好的理解WMS,首先要了解WindowManager,這一篇我們來學習WindowManager體系。

1.Window、WindowManager和WMS

Window我們應該很熟悉,它是一個抽象類,具體的實現類為PhoneWindow,它對View進行管理。 WindowManager是一個介面類,繼承自介面ViewManager,從名稱就知道它是用來管理Window的,它的實現類為WindowManagerImpl。如果我們想要對Window進行新增和刪除就可以使用WindowManager,具體的工作都是由WMS來處理的,WindowManager和WMS通過Binder來進行跨程式通訊,WMS作為系統服務有很多API是不會暴露給WindowManager的,這一點與ActivityManager和AMS的關係有些類似。
關於WMS的功能,會在後續文章進行介紹,這裡我們只需要知道它的主要功能包括Window管理和輸入系統就可以了。這一系列文章的重點是WindowManager。
Window、WindowManager和WMS的關係可以簡略的用下圖來表示。

Window包含了View並對View進行管理,Window用虛線來表示是因為Window是一個抽象概念,並不是真實存在,Window的實體其實也是View。WindowManager用來管理Window,而WindowManager所提供的功能最終會由WMS來進行處理。

2.WindowManager體系

接下來我們從原始碼角度來分析WindowManager體系以及Window和WindowManager的關係。
WindowManager是一個介面類,繼承自介面ViewManager,ViewManager中定義了三個方法,分別用來新增、更新和刪除View:
frameworks/base/core/java/android/view/ViewManager.java

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}複製程式碼

WindowManager也繼承了這些方法,而這些方法傳入的引數都是View,說明WindowManager具體管理的是以View形式存在的Window。WindowManager在繼承ViewManager的同時,又加入很多功能,包括Window的型別和層級相關的常量、內部類以及一些方法,其中有兩個方法是根據Window的特性加入的,如下所示。

    public Display getDefaultDisplay();
    public void removeViewImmediate(View view);複製程式碼

getDefaultDisplay方法會得知這個WindowManager例項將Window新增到哪個螢幕上了,換句話說,就是得到WindowManager所管理的螢幕(Display)。removeViewImmediate方法則規定在這個方法返回前要立即執行View.onDetachedFromWindow(),來完成傳入的View相關的銷燬工作。關於Window的型別和層級會在本系列後續的文章進行介紹。
Window是一個抽象類,它的具體實現類為PhoneWindow。在Activity啟動過程中會呼叫ActivityThread的performLaunchActivity方法,performLaunchActivity方法中又會呼叫Activity的attach方法,如果不瞭解這些請檢視Android深入四大元件(一)應用程式啟動過程(後篇)這篇文章。
我們從Activity的attach方法開始入手,如下所示。
frameworks/base/core/java/android/app/Activity.java

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) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);//1
        ...
         /**
         *2
         */
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
      ...複製程式碼

註釋1處建立了PhoneWindow,在註釋2處呼叫了PhoneWindow的setWindowManager方法,這個方法的具體的實現在PhoneWindow的父類Window中。
frameworks/base/core/java/android/view/Window.java

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated
            || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);//1
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//2
}複製程式碼

如果傳入的WindowManager為null,就會在註釋1處呼叫Context的getSystemService方法,並傳入服務的名稱Context.WINDOW_SERVICE("window"),具體的實現在ContextImpl中,如下所示。
frameworks/base/core/java/android/app/ContextImpl.java

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

@Override
public String getSystemServiceName(Class<?> serviceClass) {
    return SystemServiceRegistry.getSystemServiceName(serviceClass);
}複製程式碼

最終會呼叫SystemServiceRegistry的getSystemServiceName方法。
frameworks/base/core/java/android/app/SystemServiceRegistry.java

  public static String getSystemServiceName(Class<?> serviceClass) {
        return SYSTEM_SERVICE_NAMES.get(serviceClass);
    }複製程式碼

SYSTEM_SERVICE_NAMES是一個HashMap型別的資料,它用來儲存服務的名稱,那麼傳入的Context.WINDOW_SERVICE到底對應著什麼?我們接著往下看。
frameworks/base/core/java/android/app/SystemServiceRegistry.java

final class SystemServiceRegistry {
...
 private SystemServiceRegistry() { }
 static {
 ...
   registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
...
 }
}複製程式碼

SystemServiceRegistry 的靜態程式碼塊中會呼叫多個registerService方法,這裡只列舉了和本文有關的一個。registerService方法會將傳入的服務的名稱存入到SYSTEM_SERVICE_NAMES中。從上面程式碼可以看出,傳入的Context.WINDOW_SERVICE對應的就是WindowManagerImpl例項,因此得出結論,Context的getSystemService方法得到的是WindowManagerImpl例項。我們再回到Window的setWindowManager方法,在註釋1處得到WindowManagerImpl例項後轉為WindowManager型別,在註釋2處呼叫了WindowManagerImpl的createLocalWindowManager方法:

frameworks/base/core/java/android/view/WindowManagerImpl

 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }複製程式碼

createLocalWindowManager方法同樣也是建立WindowManagerImpl,不同的是這次建立WindowManagerImpl時將建立它的Window作為引數傳了進來,這樣WindowManagerImpl就持有了Window的引用,就可以對Window進行操作,比如
在Window中新增View,來檢視WindowManagerImpl的addView方法:
frameworks/base/core/java/android/view/WindowManagerImpl

   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//1
    }複製程式碼

註釋1處呼叫了WindowManagerGlobal的addView方法,其中最後一個引數mParentWindow就是Window,可以看出WindowManagerImpl雖然是WindowManager的實現類,但是卻沒有實現什麼功能,而是將功能實現委託給了WindowManagerGlobal,這裡用到的是橋接模式。關於在Window中新增View,本系列後續的文章會詳細介紹。
我們來檢視WindowManagerImpl中如何定義的WindowManagerGlobal:
frameworks/base/core/java/android/view/WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;//1
...
  private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
 ...   
}複製程式碼

可以看出WindowManagerGlobal是一個單例,說明在一個程式中只有一個WindowManagerGlobal例項。註釋1處說明WindowManagerImpl可能會實現多個Window,也就是說在一個程式中WindowManagerImpl可能會有多個例項。

通過如上的原始碼分析,Window和WindowManager的關係如下圖所示。

PhoneWindow繼承自Window,Window通過setWindowManager方法與WindowManager發生關聯。WindowManager繼承自介面ViewManager,WindowManagerImpl是WindowManager介面的實現類,但是具體的功能都會委託給WindowManagerGlobal來實現。

參考資料
《深入理解Android 卷1》
《深入理解Android 卷3》
《深入理解Android核心設計思想》第二版
《Android開發藝術探索》


歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。

相關文章