Android8.0系統源碼WMS(二)之理解WindowManagerService

一、WMS的職責

WMS是Android中重要的服務,它是WindowManager的管理者,WMS無論對于應用開發還是Framework開發都是重要的知識點,究其原因是因為WMS有很多職責,每個職責都會涉及重要且復雜的系統,這使得WMS就像一個十字路口的交通燈一樣,沒有了這個交通燈,十字路口就無法正常通車,WMS的職責只要有以下幾點。

1、窗口管理
WMS是窗口的管理者,它負責窗口的啟動、添加和刪除,另外窗口的大小和層級也是由WMS進行管理的。窗口管理的核心成員由DisplayContent、WindowToken和WindowState。
2、窗口動畫
窗口間進行切換時,使用窗口動畫可以顯得更炫一些,窗口動畫由WMS的動畫子系統來負責,動畫子系統的管理者為WindowAnimator。
3、輸入系統的中轉站
通過對窗口的觸摸從而產生觸摸事件,InputManagerService(IMS)會對觸摸事件進行處理,它會尋找一個最合適的窗口來處理觸摸反饋信息,WMS是窗口的管理者,它作為輸入系統的中轉站再合適不過。
4、Surface管理
窗口并不具備繪制的功能,因此每個窗口都需要有一塊Surface來供自己繪制,為每個窗口分配Surface時由WMS來完成的。

WMS的職責

從上圖可以看出WMS很復雜,與它關聯的由窗口管理、窗口動畫、輸入系統和Surface,它們每一個都是重要且負責的系統。

二、WMS的創建過程

1)WMS涉及的知識點非常多,但是無論這些知識點如何多,我們還是十分有必要先知道WMS時如何創建的。WMS時在SystemServer進程中創建的,SystemServer的run方法關鍵代碼如下所示:

frameworks/base/services/java/com/android/server/SystemServer.java

 private void run() {
        try {
            ...代碼省略...
            Looper.prepareMainLooper();//1創建消息Looper
            System.loadLibrary("android_servers");//2加載動態庫libandroid_servers.so
            performPendingShutdown();
            createSystemContext();//3初始化系統的Context
            mSystemServiceManager = new SystemServiceManager(mSystemContext);//4創建系統服務管理者
            mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            SystemServerInitThreadPool.get();
        } finally {
            traceEnd();
        }
        // 開啟服務
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();//5啟動引導服務
            startCoreServices();//6啟動核心服務
            startOtherServices();//7啟動其他服務,WMS在該方法中被啟動
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
      ...代碼省略...
    }

SystemServer的run方法中會調用startOtherServices方法,WMS就是在該方法中被啟動的。

2)SystemServer的startOtherServices方法如下所示:

  private void startOtherServices() {
           ...代碼省略...
            traceBeginAndSlog("InitWatchdog");
            final Watchdog watchdog = Watchdog.getInstance();//1得到Watchdog實例,該對象可以用來監視系統的一些關鍵服務的運行狀況。
            watchdog.init(context, mActivityManagerService);//2對Watchdog實例進行初始化
            traceEnd();
            traceBeginAndSlog("StartInputManagerService");
            inputManager = new InputManagerService(context);//3創建InputManagerService
            traceEnd();
            traceBeginAndSlog("StartWindowManagerService");
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());//4執行WMS的main方法,其內部會創建WMS,
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);//5將WMS注冊到服務管理器中
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);//6將IMS注冊到服務管理器中
            traceEnd();
           ...代碼省略...        
        try {
            wm.displayReady();//7初始化屏幕顯示信息
        } catch (Throwable e) {
            reportWtf("making display ready", e);
        }
           ...代碼省略...       
        try {
            wm.systemReady();//8通知WMS,系統的初始化工作已經完成,其內部調用了WindowManagerPolicy的systemReady方法
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
             ...代碼省略...       
    }

startOtherServices會獲取Watchdog實例并對實例進行初始化,創建InputManagerService實例,并在注釋4處將該實例作為參數傳遞給WindowManagerService的main方法。

3)WindowManagerService 的main方法如下:

   public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
           //1這里使用了java8中的Lambda表達式
           DisplayThread.getHandler().runWithScissors(() ->
           sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore, policy)
            , 0);//2創建WMS實例對象
        return sInstance;
    }

DisplayThread是一個單例的前臺線程,這個線程用來處理需要低延時顯示的相關操作,并只能由WindowManager、DisplayManager和InputManager實時執行快速操作。正是在DisplayThread的run方法中創建了WMS的實例,這就意味著WMS的創建時運行在android.display線程中的。
4)來看下Handler的runWithScissors方法:

frameworks/base/core/java/android/os/Handler.java

    public final boolean runWithScissors(final Runnable r, long timeout) {
        if (r == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout must be non-negative");
        }
        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }
        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }
  • 對傳入的Runnable的timeout進行判斷,如果Runnable為null或者timeout小于0則拋出異常。
  • 根據每個線程只有一個Looper的原理來判斷當前線程(system_server線程)是否是Handler所指向的線程(android.display線程),如果是則直接執行Runnable的run方法,否則調用BlockingRunnable的postAndWait方法,并將當前線程的Runnable作為參數傳遞進去。

5)BlockingRunnable是Handler的內部類。

 private static final class BlockingRunnable implements Runnable {
        private final Runnable mTask;
        private boolean mDone;

        public BlockingRunnable(Runnable task) {
            mTask = task;
        }

        @Override
        public void run() {
            try {
                mTask.run();//注釋1
            } finally {
                synchronized (this) {
                    mDone = true;
                    notifyAll();
                }
            }
        }

        public boolean postAndWait(Handler handler, long timeout) {
            if (!handler.post(this)) {//注釋2
                return false;
            }

            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                        try {
                            wait();//注釋3
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }
    }
  • 在注釋2處將當前的BlockingRunnable添加到Handler的任務隊列中,上面第3步我們知道runWithScissors方法的第二個參數為0,因此timeout等于0,這樣如果mDone為false的話會一直調用注釋3處的wait方法使得當前線程(system_server線程)進入等待狀態。
  • 在注釋1處執行了傳入的Runnable的run方法(運行在android.display線程中),執行完畢后finally代碼塊中將mDone設置為true,并調用noifyAll方法喚醒處于等待狀態的線程,這樣就不會繼續調用注釋3處的wait方法。

結合以上兩點我們可以得出一個結論,system_server線程會一直等待android.display線程執行完畢才開始執行system_server線程,這是因為android.display線程內部執行了WMS的創建,而WMS的創建優先級要更高。

6)接下來我們來查看一下WMS的構造方法。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
        ...代碼省略...
        mInputManager = inputManager; // 注釋1
        ...代碼省略...
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        mDisplays = mDisplayManager.getDisplays(); // 注釋2
        for (Display display : mDisplays) {
            createDisplayContentLocked(display); // 注釋3
        }
        ...代碼省略...
        mActivityManager = ActivityManager.getService(); // 注釋4
        ...代碼省略...
        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        ...代碼省略...
        mAnimator = new WindowAnimator(this); // 注釋5
        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);

        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        initPolicy(); // 注釋6
        Watchdog.getInstance().addMonitor(this); // 注釋7
        ...代碼省略...
    }

  • 注釋1處保存傳遞進來的IMS,這樣WMS就持有了IMS的引用。
  • 注釋2處通過DisplayManager的getDisplay方法得到Display數組(每個顯示設備都有一個Display實例),接著遍歷Display數組。
  • 注釋3處的createDisplayContentLocked方法將Display封裝成DisplayContent,DisplayContent用來描述一塊屏幕。
  • 注釋4處得到AMS實例,并賦值給mActivityManager,這樣WMS就持有了AMS的引用。
  • 注釋5處創建WindowAnimator,用于管理所有的窗口動畫。
  • 注釋6處初始化了窗口管理策略的接口類WindowManagerPolicy(WMP),它用來定義一個窗口策略所要遵循的通用規范。
  • 注釋7處調用addMonitor方法將自身添加到watchdog中,Watchdog用來監控系統的一些關鍵服務的運行狀況,這些被監控的服務都會實現Watchdog.Monitor接口。Watchdog每分鐘都會對被監控的系統服務進行檢查,如果被監控的系統服務出現了死鎖,就會殺死Watchdog所在的進程,也就是SystemServer進程。

6)繼續來看注釋6處的initPolicy方法。

    private void initPolicy() {
        UiThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
                mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);//注釋1
            }
        }, 0);
    }

initPolicy方法和此前講的WMS的main方法的實現類似,在注釋1處執行了WMP的init方法,WMP是一個接口,init方法具體在PhoneWindowManager(PWM)中實現。PWM的init方法運行在android.ui線程中,它的優先級要高于initPolicy方法所在的android.display線程,因此android.display線程要等PWM的init方法執行完畢后,處于等待狀態的android.display線程才會被喚醒從而繼續執行下面的代碼。

三、WMS的創建過程的3個線程

上面WMS創建的過程中有提到3個線程,分別是system_server、android.display和android.ui,下圖是關于這三個線程的三個步驟。


三個線程之間的關系

1)首先在system_server線程中執行了SystemServer的startOtherServices方法,在startOtherServices方法中會調用WMS的main方,main方法會創建WMS,創建的過程是在android.display線程中實現的,由于創建WMS的優先級更高,因此system_server線程要等WMS創建完成后,處于等待狀態的system_server線程才會被喚醒從而繼續執行下面的代碼。

2)在WMS的構造方法中會調用WMS的initPolicy方法,在initPolicy方法中又會調用PWM的init方法,PWM的init方法在android.ui線程中運行,它的優先級要高于android.display線程,因此"android.display"線程要等PWM的init方法執行完畢后,處于等待狀態的android.display線程才會被喚醒從而繼續執行下面的代碼。

3)PWM的init方法執行完畢后,android.display線程就完成了WMS的創建,等待的system_server線程被喚醒后繼續執行WMS的main方法后的代碼邏輯,比如在前面第二部分第2步的注釋7的地方,WMS的displayReady方法用來初始化屏幕顯示信息。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容