圖解Android - Android GUI 系統 (1) - 概論


Android的GUI系統是Android最重要也最復雜的系統之一。它包括以下部分:

  1. 窗口和圖形系統 - Window and View Manager System.
  2. 顯示合成系統 - Surface Flinger
  3. 用戶輸入系統 - InputManager System
  4. 應用框架系統 - Activity Manager System.

它們之間的關系如下圖所示

 

只有對這些系統的功能和工作原理有基本的了解,我們才能夠解答一些經常縈繞在腦海里的問題,比如說:

  1. Activity啟動過程是怎樣的?Activity的onXXX()在后台都做了什么工作?Activity的show()和hide()是如何控制的?
  2. Surface是什么時候被誰創建的?
  3. Android是如何把一個個的控件畫到Surface上的?然后顯示到手機屏幕上的?
  4. 應用程序窗口是如何獲取焦點的?用戶的按鍵(觸摸屏或鍵盤)是怎樣傳遞到當前的窗口?
  5. Android是一個多窗口的系統嗎?
  6. Android是怎么支持多屏互動的?(Wifi Display)
  7. 輸入法窗口到底屬於哪個進程?為什么不管什么應用,只要有輸入框的地方都能彈出它?

本文將從框架和流程角度出發,試圖對Android的GUI系統做一個簡要但全面的介紹,希望借此能夠幫助大家找到上述問題的答案。

所有的內容可以濃縮在下面這張圖里,里面有很多的名稱和概念我們需要事先解釋一下:

1. Window, PhoneWindow 和 Activity

  • Activity 是 Android 應用的四大組件之一 (Activity, Service,  Content Provider,  Broadcast Receiver), 也是唯一一個與用戶直接交互的組件。
  • Window 在 不同的地方有着不同的含義。在Activity里,Window 是一個抽象類,代表了一個矩形的不可見的容器,里面布局着若干個可視的區域(View). 每個Activity都會有一個Window類成員變量,mWindow. 而在WindowManagerService里,Window指的是WindowState對象,從圖中可以看出,WindowState與一個 ViewRootImpl里的mWindow對象相對應。所以說,WindowManagerService里管理的Window其實是 Acitivity的ViewRoot。我們下面提到的Window,如果沒有做特殊說明,均指的是WindowManagerService里的 ‘Window' 概念,即一個特定的顯示區域。從用戶角度來看,Android是個多窗口的操作系統,不同尺寸的窗口區域根據尺寸,位置,z-order及是否透明等參數 疊加起來一起並最終呈現給用戶。這些窗口既可以是來自一個應用,也可以來自與多個應用,這些窗口既可以顯示在一個平面,也可以是不同的平面。總而言之,窗 口是有層次的顯示區域,每個窗口在底層最終體現為一個個的矩形Buffer, 這些Buffer經過計算合成為一個新的Buffer,最終交付Display系統進行顯示。為了輔助最后的窗口管理,Android定義了一些不同的窗 口類型:
    • 應用程序窗口 (Application Window): 包括所有應用程序自己創建的窗口,以及在應用起來之前系統負責顯示的窗口。
    • 子窗口(Sub Window):比如應用自定義的對話框,或者輸入法窗口,子窗口必須依附於某個應用窗口(設置相同的token)。
    • 系 統窗口(System Window): 系統設計的,不依附於任何應用的窗口,比如說,狀態欄(Status Bar), 導航欄(Navigation Bar), 壁紙(Wallpaper), 來電顯示窗口(Phone),鎖屏窗口(KeyGuard), 信息提示窗口(Toast), 音量調整窗口,鼠標光標等等。
  • PhoneWindow 是Activity Window的擴展,是為手機或平板設備專門設計的一個窗口布局方案,就像大家在手機上看到的,一個PhoneWindow的布局大致如下:

           

2. View, DecorView, ViewGroup, ViewRoot

View 是一個矩形的可見區域。

ViewGroup 是一種特殊的View, 它可以包含其他View並以一定的方式進行布局。Android支持的布局有FrameLayout, LinearLayout, RelativeLayout 等。

DecorView 是FrameLayout的子類,FrameLayout 也叫單幀布局,是最簡單的一種布局,所有的子View在垂直方向上按照先后順序依次疊加,如果有重疊部分,后面的View將會把前面的View擋住。我們 經常看到的彈出框,把后面的窗口擋住一部分,就是用的FrameLayout布局。Android的窗口基本上用的都是FrameLayout布局, 所以DecorView也就是一個Activity Window的頂級View, 所有在窗口里顯示的View都是它的子View.

ViewRoot . 我們可以定義所有被addView()調用的View是ViewRoot, 因為接口將會生成一個ViewRootImpl 對象,並保存在WindowManagerGlobal的mRoots[] 數組里。一個程序可能有很多了ViewRoot(只要多次調用addView()), 在WindowManagerService端看來,就是多個Window。但在Activity的默認實現里,只有mDecorView 通過addView 添加到WindowManagerService里( 見如下代碼)

 //frameworks/base/core/java/android/app/Activity.java
 void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
  }

 

因此一般情況下,我們可以說,一個應用可以有多個Activity,每個 Activity 一個Window(PhoneWindow), 每個Window 有一個DecorView, 一個ViewRootImpl, 對應在WindowManagerService 里有一個Window(WindowState).

3. ViewRootImple,  WindowManagerImpl,  WindowManagerGlobals

WindowManagerImpl: 實現了WindowManager 和 ViewManager的接口,但大部分是調用WindowManagerGlobals的接口實現的。

WindowManagerGlobals: 一個SingleTon對象,對象里維護了三個數組:

  • mRoots[ ]: 存放所有的ViewRootImpl
  • mViews[ ]: 存放所有的ViewRoot
  • mParams[ ]: 存放所有的LayoutParams.

    同時,它還維護了兩個全局IBinder對象,用於訪問WindowManagerService 提供的兩套接口:

  • IWindowManager:  主要接口是OpenSession(), 用於在WindowManagerService 內部創建和初始化Session, 並返回IBinder對象。
  • ISession:  是Activity Window與WindowManagerService 進行對話的主要接口.

ViewRootImpl:

ViewRootImpl 在整個Android的GUI系統中占據非常重要的位置,如果把Activity和View 看作 'MVC' 中的V, 把各種后台服務看作Modal,ViewRootImpl 則是'MVC' 中的'C' - Controller. Controller在MVC架構里承擔着承上啟下的作用,一般來說它的邏輯最為復雜。從下圖可以看到,ViewRootImpl 與 用戶輸入系統(接收用戶按鍵,觸摸屏輸入), 窗口系統(復雜窗口的布局,刷新,動畫),顯示合成系統(包括定時器Choreograph, SurfaceFlinger), 乃至Audio系統(音效輸出)等均有密切的關聯。研究ViewRootImpl 是研究Android整個窗口系統的核心和切入點,我們將在后面詳細討論ViewRootImpl的實現和作用。

三 者( ViewRootImpl, WindowManagerImpl, WindowManagerGlobal) 都存在於應用(有Activity)的進程空間里,一個Activity對應一個WindowManagerImpl, 一個DecorView(ViewRoot),以及一個ViewRootImpl (上面說過,實際一個Activity只有一個DecorView),而WindowManagerGlobals是一個全局對象,一個應用永遠只有一 個。

注意的是,在某些情況下,一個應用可能會有幾個ViewRootImpl對象,比如說ANR是彈出的對話框,或是網頁里面一個視頻窗口 (SurfaceView), 在WindowManagerService看來,它們也是一個窗口。同時,SystemServer的進程空間也有自己的 WindowManagerGlobals 和若干個ViewRoot, 因為WindowManagerService 內部也會管理某些系統窗口,如手機頂部的StatusBar, 手機底部的NavigationBar, 以及 鎖屏(KeyGuard)窗口,這些窗口不屬於某個特定的Activity。

4. WindowManager, WindowManagerService 和 WindowManagerPolicyService

WindowManager:  是一個接口類,定義了一些接口來管理Acitivity里的窗口。WindowManager 是Android應用進程空間里的一個對象,不提供IPC服務。

WindowManagerService: 是SystemServer進程里的一個Service,它的主要功能有

  • 窗 口的顯示刷新。這里的'Window' 其實是ViewRoot, 和上面WindowManager管理的'Window' 是不一樣的,前者是實實在在要進行顯示的‘窗口’, 而后者只是一個View的容器,並不會顯示出來。大部分情況下,Android同時只有一個Activity工作,但這並不意思着只有一個Window被 顯示,Android可能會同時顯示來自相同或不同應用的多個Window,比如說,屏幕的上方有一個狀態欄,最下方有一個導航欄,有時會彈出一些對話 框,背景可能會顯示牆紙,在應用啟動過程中,會有動畫效果,這個時候兩個Activity的窗口會有所變形且同時顯示出來,這一切都需要 WindowManager來控制何時,何地,以何種方式將所有的窗口整合在一起顯示。
  • 預處理用戶輸入時間(GlobalKey? SystemKey), 並分發給合適的窗口進行處理。
  • 輸出顯示(Display)管理。包括WifiDisplay.

WindowManagerService 是Android Framework里最為龐大復雜的模塊之一,我們后面會從各個方面對它進行盡可能詳細的分析。

5. Token, WindowToken, AppWindowToken, ApplicationToken, appToken

Token在英語中表示標記,信物的意思,在代碼中,有點類似Handle,Cookie, ID, 用來標識某個特定的對象。在Android的窗口系統中,有很多的’Token', 它們代表着不同的含義。

WindowToken: 是在WindowManagerService 中定義的一個基類,顧名思義,它是用來標識某一個窗口。和下面的appWindowToken相比, 它不屬於某個特定的Activity,  比如說輸入法窗口,狀態欄窗口等等。

appWindowToken: 顧名思義,它是用來標識app, 跟准確的說法,是用來標識某個具體的Activity.

ApplicationToken: 指的是ActivityRecord 類里的Token子類。appWindowToken里的appToken也就是它。

appToken: 和applicationToken是一個意思。

下 圖描繪了各個Token之間的關系。一個Token下面帶一個WindowList隊列,里面存放着隸屬與這個Token的所有窗口。當一個Window 加入WindowManagerService 管理時,必須指定他的Token值,WindowManagerService維護着一個Token與WindowState的鍵值Hash表。

 

通過 ‘dumpsys window tokens' 我們可以列出WindowManagerService當前所有的Token 和 窗口。比如,

WINDOW MANAGER TOKENS (dumpsys window tokens)
  All tokens:
  WindowToken{4ea639c4 null}: //token = NULL
    windows=[Window{4ea7670c u0 Application Not Responding: jackpal.androidterm}, Window{4ea63a08 u0 Keyguard}]
    windowType=-1 hidden=false hasVisible=true
  AppWindowToken{4eb29760 token=Token{4eb289d4 ActivityRecord{4ea87a20 u0 com.android.launcher/com.android.launcher2.Launcher}}}: //Launcher2
    windows=[Window{4ea837c8 u0 com.android.launcher/com.android.launcher2.Launcher}]
    windowType=2 hidden=true hasVisible=true
    ...
  WindowToken{4eb1fd48 android.os.BinderProxy@4eae8a5c}:
    windows=[Window{4ea92b78 u0 PopupWindow:4ea0240c}]  //對話框
    windowType=-1 hidden=false hasVisible=false
  AppWindowToken{4eb5d6c0 token=Token{4ea35074 ActivityRecord{4ea68590 u0 jackpal.androidterm/.Term}}}:
    windows=[Window{4eb314e4 u0 jackpal.androidterm/jackpal.androidterm.Term}]
    windowType=2 hidden=false hasVisible=true
    app=true

 

6. Surface, Layer 和 Canvas, SurfaceFlinger, Region, LayerStack

在Android中,Window與Surface一一對應。 如果說Window關心的是層次和布局,是從設計者角度定義的類,Surface則從實現角度出發,是工程師關系和考慮的類。Window的內容是變化 的,Surface需要有空間來記錄每個時刻Window的內容。在Android的SurfaceFlinger實現里,通常一個Surface有兩塊 Buffer, 一塊用於繪畫,一塊用於顯示,兩個Buffer按照固定的頻率進行交換,從而實現Window的動態刷新。

Layer是SurfaceFlinger 進行合成的基本操作單元。Layer在應用請求創建Surface的時候在SurfaceFlinger內部創建,因此一個Surface對應一個 Layer, 但注意,Surface不一定對應於Window,Android中有些Surface並不跟某個Window相關,而是有程序直接創建,比如說 StrictMode, 一塊紅色的背景,用於提示示Java代碼中的一些異常, 還有SurfaceView, 用於顯示有硬件輸出的視頻內容等。

當多個Layer進行合成的時候,並不是整個Layer的空間都會被完全顯示,根據這個Layer最終的顯示效果,一個Layer可以被划分成很多的Region, Android SurfaceFlinger 定義了以下一些Region類型:

  •  TransparantRegion: 完全透明的區域,在它之下的區域將被顯示出來。
  •  OpaqueRegion: 完全不透明的區域,是否顯示取決於它上面是否有遮擋或是否透明。
  •  VisibleRegion: 可見區域,包括完全不透明無遮擋區域或半透明區域。 visibleRegion = Region - above OpaqueRegion.
  •  CoveredRegion: 被遮擋區域,在它之上,有不透明或半透明區域。
  •  DirtyRegion: 可見部分改變區域,包括新的被遮擋區域,和新的露出區域。

Android 系統支持多種顯示設備,比如說,輸出到手機屏幕,或者通過WiFi 投射到電視屏幕。Android用Display類來表示這樣的設備。不是所有的Layer都會輸出到所有的Display, 比如說,我們可以只將Video Layer投射到電視, 而非整個屏幕。LayerStack 就是為此設 計,LayerStack 是一個Display 對象的一個數值, 而類Layer里也有成員變量mLayerStack, 只有兩者的mLayerStack 值相同,Layer才會被輸出到給該Display設備。所以LayerStack 決定了每個Display設備上可以顯示的Layer數目。

SurfaceFlinger的工作內容,就是定期檢查所有Layer的參數更新(LayerStack等),計算新的DirtyRegion, 然后將結果推送給底層顯示驅動進行顯示。這里面有很多的細節,我們將在另外的章節專門研究。

上面描述的幾個概念,均是針對於顯示這個層面,更多是涉及到中下層模塊,應用層並不參與也無需關心。對於應用而言,它關心的是如何將內容畫出來。Canvas 是Java層定義的一個類,它對應與Surface上的某個區域並提供了很多的2D繪制函數(借助於底層的Skia或OpenGL)。應用只需通過 LockCanvas() 來獲取一個Canvas對象,並調用它的繪畫方法,然后 unLockCanvasAndPost()來通知底層將更新內容進行顯示。當然,並不是所有應用程序都需要直接操作Canva, 事實上只有少量應用需要直接操作Canvas, Android提供了很多封裝好的控件 Widget,應用只需提供素材,如文字,圖片,屬性等等,這些控件會調用Canvas提供的接口幫用戶完成繪制工作。

 

7. SurfaceFlinger, HWComposer, OpenGL 和 Display

SurfaceFlinger 是一個獨立的Service, 它接收所有Window的Surface作為輸入,根據ZOrder, 透明度,大小,位置等參數,計算出每個Surface在最終合成圖像中的位置,然后交由HWComposer或OpenGL生成最終的顯示Buffer, 然后顯示到特定的顯示設備上。

HWComposer 是 Andrid 4.0后推出的新特性,它定義一套HAL層接口,然后各個芯片廠商根據各種硬件特點來實現。它的主要工作是將SurfaceFlinger計算好的Layer的顯示參數最終合成到一個顯示Buffer上。注意的是,Surface Flinger 並非是HWComposer的唯一輸入,有的Surface 不由Android的WindowManager 管理,比如說攝像頭的預覽輸入Buffer, 可以有硬件直接寫入,然后作為HWComposer的輸入之一與SurfaceFlinger的輸出做最后的合成。

OpenGL 是一個2D/3D圖形庫,需要底層硬件(GPU)和驅動的支持。在Android 4.0后,它取代Skia成為Android 的2D 繪圖圖形庫,大部分的控件均改用它來實現,應用程序也可以直接調用OpenGl函數來實現復雜的圖形界面。

Display 是Android 對輸出顯示設備的一個抽象,傳統的Display 設備是手機上的LCD屏,在Andrid 4.1 后,Android 對SurfaceFlinger 進行了大量的改動,從而支持其他外部輸入設備,比如HDMI, Wifi Display 等等。Display的輸入是根據上面的LayerStack值進行過濾的所有Window的Surface, 輸出是和顯示設備尺寸相同的Buffer, 這個Buffer 最終送到了硬件的FB設備,或者HDMI設備,或者遠處的Wifi Display Sink設備進行顯示。輸入到輸出這條路徑上有SurfaceFlinger, OpenGL 和 HWComposer。

 

有了上述概念的解析,對Android的GUI 系統應該有了一些模糊的認識,接下來我們將按下面的順序將一步步深入其中的細節。

1. 圖解Android - Android GUI 系統 (2) - 窗口管理系統

在這一章里,我們將試圖解答下面幾個問題:

  • Surface是什么時候,被誰創建的?
  • 窗口是何時,何地被繪制和更新?
  • 窗口的顯示和隱藏是如何控制的?
  • 窗口的切換動畫是怎樣實現的?
  • More ... 

2. 圖解Android - Android GUI 系統 (3) - Surface Flinger (TBD)

在這一章里,我們將探討:

  • Surface 后面的Buffer 何時何地創建和銷毀,如何管理。
  • 多個Surface是如何合成在一起。
  • 怎樣實現多屏輸出?
  • More ...

3. 圖解Android - Android GUI 系統 (4) - Activity的生命周期

  • Acitivity的生命周期是怎樣的?何時創建,何時顯示,何時隱藏,何時銷毀?
  • 什么是UI thread? 為什么說不要在Andrid 的UI thread里做太多的事情?
  • More ...

4. 圖解Android - Android GUI 系統 (5) - Android的用戶輸入處理

  • 按鍵是怎么從底層driver 傳遞到具體的窗口進行處理的?

  • 全局鍵是怎么處理的?

  • ANR是如何產生的?又如何處理?

  • 等等... 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM