在圖解Android - Zygote 和 System Server 啓動分析一 文裏,咱們已經知道Android 應用程序是怎麼建立出來的,大概的流程是 ActivityManagerService -> Zygote -> Fork App, 而後應用程序在ActivityThread 中的進入loop循環等待處理來自AcitivyManagerService的消息。若是一個Android的應用有Acitivity, 那它起來後的第一件事情就是將本身顯示出來,這個過程是怎樣的? 這就是本章節要討論的話題。html
Android 中跟窗口管理相關(不包括顯示和按鍵處理)主要有兩個進程,Acitivty所在進程 和 WndowManagerService 所在進程(SystemServer). 上圖中用不一樣顏色區分這兩個進程,黃色的模塊運行在Activity的進程裏,綠色的模塊則在System Server內部,本文主要討論的是WindowManager Service。它們的分工是,Activity進程負責窗口內View的管理,而WindowManager Service 管理來自與不一樣Acitivity以及系統的的窗口。java
在圖解Android - Zygote, System Server 啓動分析中咱們已經知道,一個新的應用被fork完後,第一個調用的方法就是 ActivityThread的main(),這個函數主要作的事情就是建立一個ActivityThread線程,而後調用loop()開始等待。當收到來自 ActivityManager 的 LAUNCH_ACTIVITY 消息後,Activity開始了他的顯示之旅。下圖描繪的是Activity在顯示前的準備流程。android
圖分爲三部分, 右上角是Acitivity應用的初始化。中間部分是Acitivity 與WindowManager Service的交互準備工做,左下角是window顯示的開始。本文主要描述後兩部分,而Activity的啓動會放在圖解Android - Android GUI 系統 (4) - Activity的生命週期裏講解。shell
public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow){ ... root = new ViewRootImpl(view.getContext(), display); ... }
這裏的參數View是想要添加到WindowManagerService 的「window", 通常一個Activity只須要一個’Window', 因此,Acitivy的默認實現是將DecorView做爲」Window" 交給Window Manager Service 進行管理。Params是Layout相關的參數,裏面包含有長,寬,邊緣尺寸(Margin)等信息,mDisplay就是這個窗口想要輸出的Display設備編號,由ContextImpl傳遞過來。mParentWindow 就是Activity的成員變量mWindow,從最上面的類圖能夠很容易看出來,對於手機而言,就是一個PhoneWindow對象,對於GoogleTV,就是TVWindow對象。數據庫
int addToDisplay(in IWindow window, //提供給WMS的回調接口 in int seq, in windowManager.LayoutParams attrs, // layout參數 in int viewVisibility, in int layerStackId, // display ID out Rect outContentInsets, // WMS計算後返回這個View在顯示屏上的位置 out InputChannel outInputChannel); // 用戶輸入通道Handle
addToDisplay() 最終會調到WindowManager Service的addWindow() 接口。windows
全部的圖像顯示輸出都是由時鐘驅動的,這個驅動信號稱爲VSYNC。這個名詞來源於模擬電視時代,在那個年代,由於帶寬的限制,每一幀圖像都有分紅兩次傳輸,先掃描偶數行(也稱偶場)傳輸,再回到頭部掃描奇數行(奇場),掃描以前,發送一個VSYNC同步信號,用於標識這個這是一場的開始。場頻,也就是VSYNC 頻率決定了幀率(場頻/2). 在如今的數字傳輸中,已經沒有了場的概念,但VSYNC這一律念得於保持下來,表明了圖像的刷新頻率,意味着收到VSYNC信號後,咱們必須將新的一幀進行顯示。api
VSYNC通常由硬件產生,也能夠由軟件產生(若是夠準確的話),Android 中VSYNC來着於HWComposer,接收者沒錯,就是Choreographer。Choreographer英文意思是編舞者,跳舞很講究節奏不是嗎,必需要踩準點。Choreographer 就是用來幫助Android的動畫,輸入,仍是顯示刷新按照固定節奏來完成工做的。看看Chroreographer 和周邊的類結構。數組
從圖中咱們能夠看到, Choreographer 是ViewRootImpl 建立的(Choreographer是一個sigleton類,第一個訪問它的ViewRootImpl建立它),它擁有一個Receiver, 用來接收外部傳入的Event,它還有一個Callback Queue, 裏面存放着若干個CallbackRecord, 還有一個FrameHandler,用來handleMessage, 最後,它還跟Looper有引用關係。再看看下面這張時序圖,一切就清楚了,緩存
首先Looper調用loop() 後,線程進入進入睡眠,直到收到一個消息。Looper也支持addFd()方法,這樣若是某個fd上發生了IO操做(read/write), 它也會從睡眠中醒來。Choreographer的實現用到了這兩種方式,首先他經過某種方式獲取到SurfaceFlinger 進程提供的fd,而後將其交給Looper進行監聽,只要SurfaceFlinger往這個fd寫入VSync事件,looper便會喚醒。Lopper喚醒後,會執行onVsync()時間,這裏面沒有作太多事情,而是調用Handler接口 sendMessageAtTime() 往消息隊列裏又送了一個消息。這個消息最終調到了Handler (實際是FrameHandler)的handleCallback來完成上層安排的工做。爲何要繞這麼大個圈?爲何不在onVSync裏直接handleCallback()? 畢竟onVSync 和 handleCallback() 都在一個線程裏。這是由於MessageQueue 不光接收來自SurfaceFlinger 的VSync 事件,還有來自上層的控制消息。VSync的處理是至關頻繁的,若是不將VSync信號送人MessageQueue進行排隊,MessageQueue裏的事件就有可能得不到及時處理,嚴重的話會致使溢出。固然了,若是由於VSync信號排隊而致使處理延遲,這就是設計的問題了,這也是爲何Android文檔裏反覆強調在Activity的onXXX()裏不要作太耗時的工做,由於這些回調函數和Choreographer運行在同一個線程裏,這個線程就是所謂的UI線程。安全
言歸正傳,繼續往前,VSync事件最終在doFrame()裏調了三次doCallbacks()來完成不一樣的功能, 分別處理用戶輸入事件,動畫刷新(動畫就是定時更新的圖片), 最後執行performTraversals(),這個函數裏面主要是檢查當前窗口當前狀態,好比說是否依然可見,尺寸,方向,佈局是否發生改變(多是由前面的用戶輸入觸發的),分別調用performMeasure(), performLayout, performDraw()完成測量,佈局和繪製工做。咱們會在後面詳細學習這三個函數,這裏咱們主要看一下第一次進入performTraversals的狀況,由於第一次會作些初始化的工做,最重要的一件就是如本節標題,建立Surface對象。
回看圖2,咱們能夠看到Surface的建立不是在Activity進程裏,而是在WindowManagerService完成的(粉顏色)。當一個Activity第一次顯示的時候,Android顯示切換動畫,所以Surface是在動畫的準備過程當中建立的,具體發生在類WindowStateAnimator的createSurfaced()函數。它最終建立了一個SurfaceControl 對象。SurfaceControl是Android 4.3 裏新引進的類,Google從以前的Surface類裏拆出部分接口,變成SurfaceControl,爲何要這樣? 爲了讓結構更清晰,WindowManagerService 只能對Surface進行控制,但並不更新Surface裏的內容,分拆以後,WindowManagerService 只能訪問SurfaceControl,它主要控制Surface的建立,銷燬,Z-order,透明度,顯示或隱藏,等等。而真正的更新者,View會經過Canvas的接口將內容畫到Surface上。那View怎麼拿到WMService建立的Surface,答案是下面的代碼裏,surfaceControl 被轉換成一個Surface對象,而後傳回給ViewRoot, 前面建立的空的Surface如今有了實質內容。Surface經過這種方式被建立出來,Surface對應的Buffer 也相應的在SurfaceFlinger內部經過HAL層模塊(GRAlloc)分配並維護在SurfaceFlinger 內部,Canvas() 經過dequeueBuffer()接口拿到Surface的一個Buffer,繪製完成後經過queueBuffer()還給SurfaceFlinger進行繪製。
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); if (surfaceControl != null) { outSurface.copyFrom(surfaceControl); if (SHOW_TRANSACTIONS) Slog.i(TAG, " OUT SURFACE " + outSurface + ": copied"); } else { outSurface.release(); }
到這裏,咱們知道了Activity的三大工做,用戶輸入響應,動畫,和繪製都是由一個定時器驅動的,Surface在Activity第一次啓動時由WindowManager Service建立。接下來咱們具體看一下View是如何畫在Surface Buffer上的,而Surface Buffer的顯示則交由圖解Android - Android GUI 系統 (3) - Surface Flinger 來討論。
直接從前面提到的performMeasure()函數開始.
由於遞歸調用,實際的函數調用棧比這裏顯示的深得不少,這個函數會從view的結構樹頂(DecorView), 一直遍歷到葉節點。中間會通過三個基類,DecorView, ViewGroup 和 View, 它們的類結構以下圖所示:
全部可見的View(不包括DecorView 和 ViewGroup)都是一個矩形,Measure的目的就是算出這個矩形的尺寸, mMeasuredWidth 和 mMeasuredHeight (注意,這不是最終在屏幕上顯示的尺寸),這兩個尺寸的計算受其父View的尺寸和類型限制,這些信息存放在 MeasureSpec裏。MeasureSpec 裏定義了三種constraints,
/*父View對子View尺寸沒有任何要求,其能夠設任意尺寸*/ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /* 父View爲子View已經指定了大小*/ public static final int EXACTLY = 1 << MODE_SHIFT; /*父View沒有指定子View大小,但其不能超過父View的邊界 */ public static final int AT_MOST = 2 << MODE_SHIFT;
widthMeasureSpec 和 heightMeasureSpec 做爲 onMeasure的參數出入,子View根據這兩個值計算出本身的尺寸,最終調用 setMeasuredDimension() 更新mMeasuredWidth 和 mMeasuredHeight.
performMeasure() 結束後,全部的View都更新了本身的尺寸,接下來進入performLayout().
performLayout() 的流程和performMeasure基本上同樣,能夠將上面圖中的measure() 和 onMeasure 簡單的換成 layout() 和 onLayout(), 也是遍歷整課View樹,根據以前算出的大小將每一個View的位置信息計算出來。這裏不作太多描述,咱們把重心放到performDraw(), 由於這塊最複雜,也是最爲重要的一塊。
很早就玩過Android手機的同窗應該能體會到Android2.3 到 Android 4.0 (其實Android3.0就有了,只是這個版本只在平板上有)的性能的巨大提高,UI界面的滑動效果一下變得順滑不少,究竟是framework的什麼改動帶來的?咱們立刻揭曉。。。(這塊非本人工做領域,網上相關的資料也不多,因此純憑我的磚研,歡迎拍磚指正)
OK, 如標題所述,最根本的緣由就是引入了硬件加速, GPU是專門優化圖形繪製的硬件單元,不少GPU(至少手機上的)都支持OpenGL,一種開放的跨平臺的3D繪圖API。Android3.0之前,幾乎全部的圖形繪製都是由Skia完成,Skia是一個向量繪圖庫,使用CPU來進行運算, 因此它的performance是一個問題(固然,Skia也能夠用GPU進行加速,有人在研究,但好像GPU對向量繪圖的提高不像對Opengl那麼明顯),因此從Android3.0 開始,Google用hwui取代了Skia,準確的說,是推薦取代,由於Opengl的支持不徹底,有少許圖形api仍由Skia完成,另外還要考慮到兼容性,硬件加速的功能並非默認打開,須要程序在AndroidManifests.xml 或代碼裏控制開關。固然,大部分Canvas的基本操做都經過hwui重寫了,hwui下面就是Opengl和後面的GPU,這也是爲何Android 4.0的launcher變得異常流暢的緣故。OK,那咱們接下來的重點就是要分析HWUI的實現了。
在此以前,簡單的介紹一下OpenGL的一些概念,不然很難理解。要想深刻理解Opengl,請必讀經典的紅包書:http://www.glprogramming.com/red/
Opengl說白了,就是一組圖形繪製的API。 這些API都是一些很是基本的命令,經過它,你能夠構造出很是複雜的圖形和動畫,同時,它又是跟硬件細節無關的,因此無需改動就能夠運行在不一樣的硬件平臺上(前提是硬件支持所需特性)。OpenGL的輸入是最基本幾何元素(geometric primitives), 點(points), 線(lines), 多邊形(polygons), 以及bitmap和pixle data, 他的輸出是一個或兩個Framebuffer(真3D立體). 輸入到輸出的流程(rendering pipeline)以下圖所示:
這裏有太多的概念,咱們只描述跟本文相關的幾個:
vertex data
全部的幾何元素(點線面)均可以用點(vertics)來描述, 每一個點都對應三維空間中的一個座標(x,y,z), 以下圖所示,改變若干點的位置,咱們即可以構造出一個立體的圖形。
Triangles
OpenGL只能畫非凹(nonconvex)的多邊形,但是現實世界中存在太多的凹性的物體,怎麼辦呢?經過連線能夠將凹的物體分紅若干個三角形,三角形永遠都是凸(convex)的。同時三角形還有一個特性,三個點能夠惟一肯定一個平面,因此用盡量多的三角形就能夠逼近現實世界中複雜的曲線表面,好比下圖的例子,三角形的數目越多,球體的表示就越逼真。這也是爲何咱們常常看到顯卡的性能評測都以三角形的生成和處理做爲一個很是重要的指標。
Display List
全部的Vertex和Pixel信息都可以存在Display List 裏面,用於後續處理,換句話說,Display List 就是OpenGL命令的緩存。Display List的使用對OpenGL的性能提高有很大幫助。這個很容易理解,想象一個複雜的物體,須要大量的OpenGL命令來描繪,若是畫一次都須要從新調用OpenGL API,並把它轉換成Vertex data,顯然是很低效的,若是把他們緩存在Display List裏,須要重繪的時候,發一個個命令通知OpenGL直接從Display List 讀取緩存的Vertex Data,那勢必會快不少,若是考慮到Opengl是基於C/S架構,能夠支持遠程Client,這個提高就更大了。Display也能夠緩存BitMap 或 Image, 舉個例子,假設要顯示一篇文章,裏面有不少重複的字符,若是每一個字符都去字庫讀取它的位圖,而後告訴Opengl去畫,那顯然是很慢的。但若是將整個字庫放到Display List裏,顯示字符時候只須要告訴Opengl這個字符的偏移量,OpenGL直接訪問Display List,那就高效多了。
Pixel Data
Pixle data 包括位圖(bitmap), Image, 和任何用於繪製的Pixel數據(好比Fonts)。一般是以矩陣的形式存放在內存當中。經過Pxiel data, 咱們避免大量的圖形繪製命令。同時經過現實世界中獲取的紋理圖片,能夠將最終的物體渲染得更逼真。好比說畫一堵牆,若是沒有pixel data,咱們須要將每塊磚頭都畫出來,也就是說須要大量的Vertex。但是若是經過一張現實生活中拍攝的磚牆的圖片,只須要4個點畫出一個大矩形,而後上面貼上紋理,顯然,速度和效果都要好得多。
FrameBuffer
Framebuffer就是Opengl用來存儲結果的buffer。Opengl的frameBuffer類型有幾種。Front Buffer 和 Back Buffer, 分別用於顯示和繪製,二者經過swapBuffer 進行交換。Left Buffer 和 Right buffer, 用於真立體(須要帶眼鏡的那種) 圖像的左右眼Buffer,Stencil buffer, 用於禁止在某些區域上進行繪製,想像一下若是在一件T恤上印上圖案,你是否是須要一個鏤空的紙板?這個紙板就是stencil buffer.
OK, 對Opengl rendering pipeline簡單介紹到此,有興趣的同窗能夠閱讀opengl的紅包書或運行一些簡單的例子來深刻理解Opengl。回到主題,僅僅使用Opengl 和 GPU 取代Skia 就可以大幅提高性能?答案固然不是,性能的優化很大程度上取決於應用,應用必須正確的使用Opengl命令才能發揮其最大效能。Android從pipeline 角度提供了兩種機制來提高性能,一個就是咱們剛纔說到的Display List,另外一個叫 Hardware Layer, 其實就是緩存的FrameBuffer, 好比說Android的牆紙,通常來講,他是不會發生變化的,所以咱們能夠將它緩存在Hardware Layer裏,這張就不須要每次進行拷貝和重繪,從而大幅提高性能。
說白了,優化圖形性能的核心在於 1)用硬件來減小CPU的參與,加速圖形計算。 2)從軟件角度,經過Display List 和 Hardware Layer, 將已經完成的工做盡量的緩存起來,只作必需要作的事情,儘量的減小運算量。
接下來看實現吧。
這塊代碼至關的複雜,花了兩天時間才把下面的圖整理出來,但仍是沒有把細節徹底吃透,簡單的介紹一下框架和流程吧,若是有須要你們能夠下來細看代碼。
圖中上半部爲Java 代碼,下半部爲Native層。先介紹裏面出現的一些概念:
Canvas
Canvas是Java層獨有的概念,它爲View提供了大部分圖形繪製的接口。這個類主要用於純軟件的繪製,硬件加速的圖形繪製則由HardwareCanvas取代。
HardwareCanvas,GLES20Canvas, GLES20RecordingCanvas
hardwareCanvas是一個抽象類,若是系統屬性和應用程序指定使用硬件加速(現已成爲默認),它將會被View(經過AttachInfo,如 下圖所示) 引用來完成全部的圖形繪製工做。GLES20Canvas 則是hardwareCanvas的實現,但它也只是一層封裝而已,真正的實如今Native 層,經過jni (andriod_view_gles20Canvas.cpp)接口來訪問底層的Renderer, 進而執行OpenGL的命令。 此外,GLES20Canvas還提供了一些靜態接口,用於建立各種Renderer對象。
GLES20RecordingCanvas 繼承GLES20Canvas, 經過它調用的OpenGL命令將會存儲在DisplayList裏面,而不會當即執行。
HardwareRenderer, GLRender, GL20Renderer
這三個類都是Java的Wrapper類,經過訪問各類Canvas來控制繪製流程。詳見下面兩張時序圖。
OpenGLRenderer, DisplayListRenderer, HardwareLayerRenderer
Java的HardwareCanvas 和 HardwareRenderer在底層的對應實現。OpenGLRenderer是基類,只有它直接訪問底層OpenGL庫。DisplayListRenderer 將View經過GLES20Canvas傳過來的OpenGL 命令存在OpenGL的DisplayList中。而HardwareLayerRenderer 管理HardwareLayer的資源。
GLES20
就是OpenGL ES 2.0 的API。它的實現通常由GPU的設計廠家提供,能夠在設備的/system/lib/egl/ 找到它的so,名字爲 libGLES_xxx.so, xxx 就是特定設備的代號,好比說,libGLES_gc.so 就是Vivante公司的GPU實現,libGLESv2_mali.so 就是ARM公司提供的Mali GPU的實現。它也能夠由軟件實現,好比說 libGLES_android.so, 是Google提供的軟件實現。
EGL
雖然對於上層應用來講OpenGL接口是跨平臺的,可是它的底層(GPU)實現和平臺(SoC)是緊密相關的,因而OpenGL組織定義一套接口用來訪問平臺本地的窗口系統(native platform window system),這套接口就是EGL,好比說 eglCreateDisplay(), eglCreateSurface(), eglSwapBuffer()等等。EGL的實現通常明白libEGL.so, 放在/system/lib/egl/ 下面。
View, Canvas, Renderer, DisplayList, HardwareLayer 的關係以下圖所示:
每一個View都對應一個DisplayList, 在Native層代碼裏管理。每一個View經過GLESRecordingCanvas 以及Native層對應的DisplayRenderer 將OpenGL命令存入DisplayList.最後View 經過GLES20Canvas 通知OpenGLRenderer 執行這些DisplayList 裏面的OpenGL 命令。
他們的生命週期以下圖所示 (粉紅表明 New, 黑色表明 Delete, 黃色表明Java類,藍色表明C++, 綠色表明JNI).
等等!好像少了點什麼,怎麼沒有DisplayList? 前面不是說它是性能優化的幫手之一嗎?對了,上面只介紹了繪製的開始和結尾,在View的生命週期中,還有最重要的一步,Draw 尚未被介紹,DisplayList 相關的操做就是在Draw()裏面完成的。
繞了好大一圈,終於回到最初的話題,Android是怎樣將View畫出來的? 讓咱們按照圖中的序號一一進行講解。(黃色:Java, 綠色:C++,藍色:JNI,粉色:New, 黑色:Delete).
即使是使用了DisplayList, 對於複雜的圖形,仍然須要執行大量的OpenGL命令,若是須要對這一部分進行優化,就須要使用到 HardwareLayer對繪製的圖形進行緩存,若是圖形不發生任何變化,就不須要執行任何OpenGL命令,而是將以前緩存在GPU內存的Buffer 直接與其餘View進行合成,從而大大的提升性能。這些存儲在GPU內部的Buffer就稱爲 Hardware Layer。除了Hardware Layer, Android 還支持Software Layer,和Hardware Layer 不一樣之處在於,它不存在於GPU內部,而是存在CPU的內存裏,所以它不通過前面所說的 Hardware Render Pipeline, 而是走Android最初的軟件Render pipeline。無論是Hardware Layer 仍是 Software Layer, 在Draw() 內部均稱爲Cache,只要有Cache的存在,相對應的View將不用重繪,而是使用已有的Cache。Hardware Layer, Software Layer 和 Display List 是互斥的,同時只能有一種方法生效(固然,Hardware Layer的第一次繪製仍是經過Display List 完成),下表總結了它們的差異, 從中能夠看到,Hardware Layer 對性能的提高是最大的,惟一的問題是佔用GPU的內存(這也是爲何顯卡的內存變得愈來愈大的緣由之一),因此通常來講,Hardware Layer使用在那些圖片較爲複雜,但不常常改變,有動畫操做或與其餘窗口有合成的場景,好比說WallPaper, Animation 等等。
Enabled if | GPU accelerated? | Cached in | Performance(from Google I/O 2001) |
Usage | |
Display List | Hardware Accelerated = True | Y | GPU DisplayList | 2.1 | Complex View |
Hardware Layer | LayerType = HARDWARE | Y | GPU Memory | 0.009 | Complex View, Color Filter(顏色過濾), Alpha blending (透明度設置), etc. |
Software Layer | LayerType = SOFTWARE | N | CPU Memory | 10.3 | No Hardware Accelerated, Color filter, Alpha blending, etc. |
前面介紹了View的第一次繪製的過程。可是一個View在運行中終究是要發生變化的,好比說,用戶在TextView的文字發生了改變,或者動畫致使View的尺寸發生變化,再或者說一個對話框彈出而後又消失,被遮擋的部分從新露了出來,這些都須要對View進行重繪。在介紹重繪以前,咱們先了解一下View內部的一些Flag 定義。
Flags | == 1 | Set at | Clear at |
PFFLAG_HAS_BOUNDS | 1: View has size and Position set. |
View::setFrame() | |
PFFLAG_DRAWN | 1: Has been drawn, only after which, invalidate() is valid. |
ViewGroup::addViewInLayout |
View::invalidate() |
PFFLAG_DRAWING_CACHE_VALID | 1: Has DisplayList / Hardware Layer / Software Layer |
View::GetHardwareLayer() |
View::invalidate(true) |
PFFLAG_INVALIDATED | View is specifically invalidated, not just dirty(child for instance). DisplayList will be recreated if set. |
ViewGroup::addViewInLayout ViewGroup::attachViewToParent View::force/requestLayout() View::invalidate() |
View::draw() |
PFLAG_DIRTY | Set to indicate that the view need to be redrawn. (use displaylist cache if PFFLAG_INVALIDATED flag is false) |
View::invalidate() | ViewGroup::addViewInLayout |
PFLAG_DIRTY_OPAQUE | DIRTY because of OPAQUE (hidden by others). | View::invalidateChild() | ? |
從上表能夠看出,View經過內部這些Flag來控制重繪。基本上重繪分兩種狀況,一種是須要從新生成DisplayList, 另一種是使用以前已有的Cache,包括DisplayList或者是Hardware Layer。使用哪一種重繪方式由當前View的Flags,以及應用程序傳入的參數決定。控制它的就是一組Invalidate() 函數。
void invalidate(boolean invalidateCache) { if (skipInvalidate()) { //若是View不可見,而且再也不動畫退出過程當中(fade out),將不執行Invalidate(). return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || //DRAWN -> 已經被Draw()過 (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || //有Cache,且被要求從新刷新Cache (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) //沒有正在Invalidate()中 { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; //標記未來清除Cache,若是爲false,則有系統根據Dirty Region決定是否須要從新生成DisplayList。 } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { //系統不支持Dirty Region,必須重繪整個區域, 基本不會進去 } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); p.invalidateChild(this, r); //通知兄弟view(有共同的ViewParent(ViewGroup 或者 ViewRoot)進行 Invalidate. } } }
假如全部的條件都支持重繪,便會調用到ViewParent的invalidateChild()方法。(ViewParent是一個接口類,它的實現類是ViewGroup 和 ViewRootImpl。)這個方法會從當前View開始,向上遍歷到ViewRoot 或者 到某個ViewGroup的區域與當前View的Dirty區域沒有重疊爲止。途中的每一個ViewGroup都會被標記上Dirty。在接下來VSYNC的performDraw()裏,ViewRootImpl 會遍歷全部標記Dirty的ViewGroup,而後找到裏面標記Dirty的View,只有這些View的DisplayList 被重建,而其餘實際上沒有變化的View(雖然它們在同一個ViewGroup裏面),若是沒有Hardware Layer, 只需從新執行對應Display List 裏面的OpenGL 命令。經過這種方式,Android只重繪須要重繪的View,從軟件層面將GPU的輸入最小化,從而優化圖形性能。
到此,咱們已經瞭解了一個Acitivty(Window)是如何畫出來的,讓咱們在簡要重溫一下這個過程:
注意的是,上面討論的只是一個窗口的流程,而Android是個多窗口的系統,窗口之間可能會有重疊,窗口切換會有動畫產生,窗口的顯示和隱藏都有可能會致使資源的分配和釋放,這一切須要有一個全局的服務進行統一的管理,這個服務就是咱們大名鼎鼎的Window Manager Service (簡寫 WMS).
其實Window Manager Service 的工做不只僅是管理窗口,還會跟不少其餘服務打交道,如 InputManager Service, AcitivityManager Service 等等,但本章只討論它在Window Manager 方面的工做,下圖中紅色標記部分。
首先來看Layout。Layout 是Window Manager Service 重要工做之一,它的流程以下圖所示:
Android裏定義了不少區域,以下圖所示
Overscan:
Overscan 是電視特有的概念,上圖中黃色部分就是Overscan區域,指的是電視機屏幕四周某些不可見的區域(由於電視特性,這部分區域的buffer內容顯示時被丟棄),也意味着若是窗口的某些內容畫在這個區域裏,它在某些電視上就會看不到。爲了不這種狀況發生,一般要求UI不要畫在屏幕的邊角上,而是預留必定的空間。由於Overscan的區域大小隨着電視不 同而不一樣,它通常由終端用戶經過UI指定,(好比說GoogleTV裏就有肯定Overscan大小的應用)。
OverscanScreen, Screen:
OverscanScreen 是包含Overscan區域的屏幕大小,而Screen則爲去除Overscan區域後的屏幕區域, OverscanScreen > Screen.
Restricted and Unrestricted:
某些區域是被系統保留的,好比說手機屏幕上方的狀態欄(如圖紙綠色區域)和下方的導航欄,根據是否包括這些預留的區域,Android把區域分爲Unrestricted Area 和 Resctrited Aread, 前者包括這部分預留區域,後者則不包含, Unrestricted area > Rectricted area。
mFrame, mDisplayFrame, mContainingFrame
Frame指的是一片內存區域, 對應於屏幕上的一塊矩形區域. mFrame的大小就是Surface的大小, 如上上圖中的藍色區域. mDisplayFrame 和 mContainingFrame 通常和mFrame 大小一致. mXXX 是Window(ViewRootImpl, Windowstate) 裏面定義的成員變量.
mContentFrame, mVisibleFrame
一個Surface的全部內容不必定在屏幕上都獲得顯示, 與Overscan重疊的部分會被截掉, 系統的其餘窗口也會遮擋掉部分區域 (好比短信窗口,ContentFrame是800x600(沒有Status Bar), 但當輸入法窗口彈出是,變成了800x352), 剩下的區域稱爲Visible Frame, UI內容只有畫在這個區域裏才能確保可見. 因此也稱爲Content Frame. mXXX也是Window(ViewRootImpl, WindowState) 裏面定義的成員變量.
Insects
insets的定義如上圖所示, 用了表示某個Frame的邊緣大小.
Layout 在WMS 內部的時序以下圖所示,外部調整Overscan參數或View內部主動調用requestLayout() 都會觸發WMS的從新layout,layout完成後,WMS會經過IWindow的resized()接口通知ViewRoot, 最終會調用requestLayout(), 並在下一個VSYNC 事件到來時更新。
。
計算Layout主要有圖中三個紅色的函數完成,它們代碼不少,涉及到不少計算,但只要對着咱們上面給的三個圖來看,不難看出它的意思,本文將不詳細深刻。
Animation的原理很簡單,就是定時重繪圖形。下面的類圖中給出了Android跟Animation相關的類。
Animation:
Animation抽象類,裏面最重要的一個接口就是applyTranformation, 它的輸入是當前的一個描述進度的浮點數(0.0 ~ 1.0), 輸出是一個Transformation類對象,這個對象裏有兩個重要的成員變量,mAlpha 和 mMatrix, 前者表示下一個動畫點的透明度(用於灰度漸變效果),後者則是一個變形矩陣,經過它能夠生成各類各樣的變形效果。Android提供了不少Animation的具體實現,好比RotationAnimation, AlphaAnimation 等等,用戶也能夠實現本身的Animation類,只須要重載applyTransform 這個接口。注意,Animation類只生成繪製動畫所需的參數(alpha 或 matrix),不負責完成繪製工做。完成這個工做的是Animator.
Animator:
控制動畫的‘人’, 它一般經過向定時器Choreographer 註冊一個Runnable對象來實現定時觸發,在回調函數裏它要作兩件事情:1. 從Animation那裏獲取新的Transform, 2. 將Transform裏的值更新底層參數,爲接下來的重繪作準備。動畫能夠發生在Window上,也能夠發生在某個具體的View。前者的動畫會經過SurfaceControl直接在某個Surface上進行操做(會在SurfaceFlinger裏詳細描述),好比設置Alpha值。後者則經過OpenGL完成(生成咱們前面提過的DisplayList).
WindowStateAnimator, WindowAnimator, AppWindowAnimator:
針對不一樣對象的Animator. WindowAnimator, 負責整個屏幕的動畫,好比說轉屏,它提供Runnable實現。WindowStateAnimator, 負責ViewRoot,即某一個窗口的動畫。AppWindowAnimator, 負責應用啓動和退出時候的動畫。這幾個Animator都會提供一個函數,stepAnimationLocked(), 它會完成一個動畫動做的一系列工做,從計算Transformation到更新Surface的Matrix.
具體來看一下Window的Animation和View的Animation
View 的動畫實現步驟與Windows 相似,有興趣的同窗能夠去看View.java 的 drawAnimation() 函數。
WMS 裏面管理着各式各樣的窗口, 以下表所示(在WindowManagerService.java 中定義)
類型 | 用途 | |
mAnimatingAppToken | ArrayList<AppWindowToken> | 正在動畫中的應用 |
mExistingAppToken | ArrayList<AppWindowToken> | 退出但退出動畫尚未完成的應用。 |
mResizingWindows | ArrayList<WindowState> | 尺寸正在改變的窗口,當改變完成後,須要通知應用。 |
mFinishedStarting | ArrayList<AppWindowToken> | 已經完成啓動的應用。 |
mPendingRemove | ArrayList<WindowState> | 動畫結束的窗口。 |
mLosingFocus | ArrayList<WindowState> | 失去焦點的窗口,等待得到焦點的窗口進行顯示。 |
mDestorySurface | ArrayList<WindowState> | 須要釋放Surface的窗口。 |
mForceRemoves | ArrayList<WindowState> | 須要強行關閉的窗口,以釋放內存。 |
mWaitingForDrawn | ArrayList<Pair<WindowState, IRemoteCallback>> | 等待繪製的窗口 |
mRelayoutWhileAnimating | ArrayList<WindowState> | 請求relayout但此時仍然在動畫中的窗口。 |
mStrictModeFlash | StrictModeFlash | 一個紅色的背景窗口,用於提示可能存在的內存泄露。 |
mCurrentFocus | WindowState | 當前焦點窗口 |
mLastFocus | WindowState | 上一焦點窗口 |
mInputMethodTarget | WindowState | 輸入法窗口下面的窗口。 |
mInputMethodWindow | WindowState | 輸入法窗口 |
mWallpaperTarget | WindowState | 牆紙窗口 |
mLowerWallpaperTarget | WindowState | 牆紙切換動畫過程當中Z-Order 在下面的窗口 |
mHigherWallpaperTarget | WindowState | 牆紙切換動畫過程當中Z-Order 在上面的窗口 |
能夠看到這裏大量的用到了隊列,不一樣的窗口,或同一窗口在不一樣的階段,可能會出如今不一樣的隊列裏。另外由於WindowManager Service 的服務可能被不少個線程同時調用,在這種複雜的多線程環境裏,經過鎖來實現線程安全很是難以實現,一不當心就可能致使死鎖,因此在 WindowManager 內專門有一個執行線程(WM Thread)來將全部的服務請求經過消息進行異步處理,實現調用的序列化。隊列是實現異步處理的經常使用手段。隊列加Looper線程是Android 應用經常使用的設計模型。
此外,WindowManager還根據Window的類型進行了分類(在WindowManager.java),以下表,
類型 | 常量範圍 | 子類 | 常量值 | 說明 | 例子 |
APPLICATION_WINDOW | 1~99 | TYPE_BASE_APPLICATION | 1 | ||
TYPE_APPLICATION | 2 | 應用窗口 | 大部分的應用程序窗口 | ||
TYPE_APPLICATION_STARTING | 3 | 應用程序的Activity顯示以前由系統顯示的窗口 | |||
LAST_APPLICATION_WINDOW | 99 | ||||
SUB_WINDOW | 1000~1999 | FIRST_SUB_WINDOW | 1000 | ||
TYPE_APPLICATION_PANEL | 1000 | 顯示在母窗口之上,遮擋其下面的應用窗口。 | |||
TYPE_APPLICATION_MEDIA | 1001 | 顯示在母窗口之下,若是應用窗口不挖洞,即不可見。 | SurfaceView,在小窗口顯示時設爲MEDIA, 全屏顯示時設爲PANEL | ||
TYPE_APPLICATION_SUB_PANEL | 1002 | ||||
TYPE_APPLICATION_ATTACHED_DIALOG | 1003 | ||||
TYPE_APPLICATION_MEIDA_OVERLAY | 1004 | 用於兩個SurfaceView的合成,若是設爲MEDIA, 則上面的SurfaceView 擋住下面的SurfaceView |
|||
SYSTEM_WINDOW | 2000~2999 | TYPE_STATUS_BAR | 2000 | 頂部的狀態欄 | |
TYPE_SEARCH_BAR | 2001 | 搜索窗口,系統中只能有一個搜索窗口 | |||
TYPE_PHONE | 2002 | 電話窗口 | |||
TYPE_SYSTEM_ALERT | 2003 | 警告窗口,在全部其餘窗口之上顯示 | 電量不足提醒窗口 | ||
TYPE_KEYGUARD | 2004 | 鎖屏界面 | |||
TYPE_TOAST | 2005 | 短時的文字提醒小窗口 | |||
TYPE_SYSTEM_OVERLAY | 2006 | 沒有焦點的浮動窗口 | |||
TYPE_PRIORITY_PHONE | 2007 | 緊急電話窗口,能夠顯示在屏保之上 | |||
TYPE_SYSTEM_DIALOG | 2008 | 系統信息彈出窗口 | 好比SIM插上後彈出的運營商信息窗口 | ||
TYPE_KEYGUARD_DIALOG | 2009 | 跟KeyGuard綁定的彈出對話框 | 鎖屏時的滑動解鎖窗口 | ||
TYPE_SYSTEM_ERROR | 2010 | 系統錯誤提示窗口 | ANR 窗口 | ||
TYPE_INPUT_METHOD | 2011 | 輸入法窗口,會擠佔當前應用的空間 | |||
TYPE_INPUT_METHOD_DIALOG | 2012 | 彈出的輸入法窗口,不會擠佔當前應用窗口空間,在其之上顯示 | |||
TYPE_WALLPAPER | 2013 | 牆紙 | |||
TYPE_STATUS_BAR_PANEL | 2014 | 從狀態條下拉的窗口 | |||
TYPE_SECURE_SYSTEM_OVERLAY | 2015 | 只有系統用戶能夠建立的OVERLAY窗口 | |||
TYPE_DRAG | 2016 | 浮動的可拖動窗口 | 360安全衛士的浮動精靈 | ||
TYPE_STATUS_BAR_PANEL | 2017 | ||||
TYPE_POINTER | 2018 | 光標 | |||
TYPE_NAVIGATION_BAR | 2019 | ||||
TYPE_VOLUME_OVERLAY | 2020 | 音量調節窗口 | |||
TYPE_BOOT_PROGRESS | 2021 | 啓動進度,在全部窗口之上 | |||
TYPE_HIDDEN_NAV_CONSUMER | 2022 | 隱藏的導航欄 | |||
TYPE_DREAM | 2023 | 屏保動畫 | |||
TYPE_NAVIGATION_BAR_PANEL | 2024 | Navigation bar 彈出的窗口 | 好比說應用收集欄 | ||
TYPE_UNIVERSAL_BACKGROUND | 2025 | ||||
TYPE_DISPLAY_OVERLAY | 2026 | 用於模擬第二顯示設備 | |||
TYPE_MAGNIFICATION | 2027 | 用於放大局部 | |||
TYPE_RECENTS_OVERLAY | 2028 | 當前應用窗口,多用戶狀況下只顯示在用戶節目 |
windowManager Service 會根據窗口的類型值來決定Z-Order (於常量值無關,值大說明是後面Android版本添加的,好比說2025~2028就是4.3 新加的)。好比說SurfaceView.java 裏的一個函數,
public void setZOrderOnTop(boolean onTop) { if (onTop) { mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; //PANEL在上面 // ensures the surface is placed below the IME mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else { mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; //MEDIA類型窗口在應用窗口之下,應用必需挖洞(設Alpha值)才能露出它。 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } }
這些類型最終在WindowManager 內部轉換成幾個Z-Order 值,mBaseLayer, mSubLayer, mAnimationLayer, 分別代表主窗口,子窗口(附加在主窗口之上),和動畫窗口的Z-Order值(越大越在上邊)。不一樣的窗口類型在不一樣的硬件產品上有不一樣的定義,所以它是實如今WindowManagerPolicy裏的windowTypeToLayerLw(), 舉PhoneWindowManager 爲例,它的ZOrder 順序是:
Univese background < Wallpaper < Phone < Search Bar < System Dialog < Input Method Window < Keyguard < Volume < System Overlay < Navigation < System Error < < Display Overlay< Drag < Pointer < Hidden NAV consumer,
因此,咱們若是要在手機鎖屏時顯示歌曲播放進度,就必須給這個窗口分配一個大於Keyguard的type,如 system overlay 等。
一個Window能夠有若干個Sub Window, 他們和主窗口的ZOrder關係是
Media Sublayer(-2) < Media Overlay sublayer (-1) < Main Layer(0) < Attached Dialog (1) < Sub panel Sublayer (2)
經過 "adb shell dumpsys window" 能夠查看系統當前運行的窗口的ZOrder 和 Visibility, 好比下面就是在短信輸入界面下運行「dumpsys" 得到的結果,
1 Window #0 Window{4ea4e178 u0 Keyguard}: 2 mBaseLayer=121000 mSubLayer=0 mAnimLayer=121000+0=121000 mLastLayer=121000 3 mViewVisibility=0x8 mHaveFrame=true mObscured=false 4 Window #1 Window{4ea4aa7c u0 InputMethod}: 5 mBaseLayer=101000 mSubLayer=0 mAnimLayer=21020+0=21020 mLastLayer=21020 6 mViewVisibility=0x0 mHaveFrame=true mObscured=false 7 Window #2 Window{4ec1a150 u0 com.android.mms/com.android.mms.ui.ComposeMessageActivity}: 8 mBaseLayer=21000 mSubLayer=0 mAnimLayer=21015+0=21015 mLastLayer=21015 9 mViewVisibility=0x0 mHaveFrame=true mObscured=false 10 Window #3 Window{4ea7c714 u0 com.android.mms/com.android.mms.ui.ConversationList}: 11 mBaseLayer=21000 mSubLayer=0 mAnimLayer=21010+0=21010 mLastLayer=21015 12 mViewVisibility=0x8 mHaveFrame=true mObscured=true 13 Window #4 Window{4eaedefc u0 com.android.launcher/com.android.launcher2.Launcher}: 14 mBaseLayer=21000 mSubLayer=0 mAnimLayer=21005+0=21005 mLastLayer=21010 15 mViewVisibility=0x8 mHaveFrame=true mObscured=true 16 Window #5 Window{4ea17064 u0 jackpal.androidterm/jackpal.androidterm.Term}: 17 mBaseLayer=21000 mSubLayer=0 mAnimLayer=21000+0=21000 mLastLayer=22000 18 mViewVisibility=0x8 mHaveFrame=true mObscured=true
能夠看到:
因此,WindowManager Service 是經過調整窗口的mViewVisibility 和 mLayer 值來實現窗口重疊。最後給出跟Z-order相關的類圖。
圖中序號表示輸入法窗口找到它的目標窗口的過程:
WindowManager Service的介紹暫告一段落,它與其餘重要的Service,SurfaceFlinger, ActivityManager, InputManager, PowerManager, WatchDog 之間的關係將在其餘文章介紹。