Android 的窗口管理系統(tǒng) (View, Canvas, WindowManager)在圖解Android - Zygote 和 System Server 啟動分析一 文里,我們已經(jīng)知道Android 應用程序是怎么創(chuàng)建出來的,大概的流程是 ActivityManagerService -> Zygote -> Fork App, 然后應用程序在ActivityThread 中的進入loop循環(huán)等待處理來自AcitivyManagerService的消息。如果一個Android的應用有Acitivity, 那它起來后的第一件事情就是將自己顯示出來,這個過程是怎樣的? 這就是本章節(jié)要討論的話題。 Android 中跟窗口管理相關(不包括顯示和按鍵處理)主要有兩個進程,Acitivty所在進程 和 WndowManagerService 所在進程(SystemServer). 上圖中用不同顏色區(qū)分這兩個進程,黃色的模塊運行在Activity的進程里,綠色的模塊則在System Server內(nèi)部,本文主要討論的是WindowManager Service。它們的分工是,Activity進程負責窗口內(nèi)View的管理,而WindowManager Service 管理來自與不同Acitivity以及系統(tǒng)的的窗口。 1. Acitivty顯示前的準備工作在圖解Android - Zygote, System Server 啟動分析中我們已經(jīng)知道,一個新的應用被fork完后,第一個調(diào)用的方法就是 ActivityThread的main(),這個函數(shù)主要做的事情就是創(chuàng)建一個ActivityThread線程,然后調(diào)用loop()開始等待。當收到來自 ActivityManager 的 LAUNCH_ACTIVITY 消息后,Activity開始了他的顯示之旅。下圖描繪的是Activity在顯示前的準備流程。 圖分為三部分, 右上角是Acitivity應用的初始化。中間部分是Acitivity 與WindowManager Service的交互準備工作,左下角是window顯示的開始。本文主要描述后兩部分,而Activity的啟動會放在圖解Android - Android GUI 系統(tǒng) (4) - Activity的生命周期里講解。
2. Choreographer 和 Surface的創(chuàng)建所有的圖像顯示輸出都是由時鐘驅動的,這個驅動信號稱為VSYNC。這個名詞來源于模擬電視時代,在那個年代,因為帶寬的限制,每一幀圖像都有分成兩次傳輸,先掃描偶數(shù)行(也稱偶場)傳輸,再回到頭部掃描奇數(shù)行(奇場),掃描之前,發(fā)送一個VSYNC同步信號,用于標識這個這是一場的開始。場頻,也就是VSYNC 頻率決定了幀率(場頻/2). 在現(xiàn)在的數(shù)字傳輸中,已經(jīng)沒有了場的概念,但VSYNC這一概念得于保持下來,代表了圖像的刷新頻率,意味著收到VSYNC信號后,我們必須將新的一幀進行顯示。 VSYNC一般由硬件產(chǎn)生,也可以由軟件產(chǎn)生(如果夠準確的話),Android 中VSYNC來著于HWComposer,接收者沒錯,就是Choreographer。Choreographer英文意思是編舞者,跳舞很講究節(jié)奏不是嗎,必須要踩準點。Choreographer 就是用來幫助Android的動畫,輸入,還是顯示刷新按照固定節(jié)奏來完成工作的??纯碈hroreographer 和周邊的類結構。
從圖中我們可以看到, Choreographer 是ViewRootImpl 創(chuàng)建的(Choreographer是一個sigleton類,第一個訪問它的ViewRootImpl創(chuàng)建它),它擁有一個Receiver, 用來接收外部傳入的Event,它還有一個Callback Queue, 里面存放著若干個CallbackRecord, 還有一個FrameHandler,用來handleMessage, 最后,它還跟Looper有引用關系。再看看下面這張時序圖,一切就清楚了,
首先Looper調(diào)用loop() 后,線程進入進入睡眠,直到收到一個消息。Looper也支持addFd()方法,這樣如果某個fd上發(fā)生了IO操作(read/write), 它也會從睡眠中醒來。Choreographer的實現(xiàn)用到了這兩種方式,首先他通過某種方式獲取到SurfaceFlinger 進程提供的fd,然后將其交給Looper進行監(jiān)聽,只要SurfaceFlinger往這個fd寫入VSync事件,looper便會喚醒。Lopper喚醒后,會執(zhí)行onVsync()時間,這里面沒有做太多事情,而是調(diào)用Handler接口 sendMessageAtTime() 往消息隊列里又送了一個消息。這個消息最終調(diào)到了Handler (實際是FrameHandler)的handleCallback來完成上層安排的工作。為什么要繞這么大個圈?為什么不在onVSync里直接handleCallback()? 畢竟onVSync 和 handleCallback() 都在一個線程里。這是因為MessageQueue 不光接收來自SurfaceFlinger 的VSync 事件,還有來自上層的控制消息。VSync的處理是相當頻繁的,如果不將VSync信號送人MessageQueue進行排隊,MessageQueue里的事件就有可能得不到及時處理,嚴重的話會導致溢出。當然了,如果因為VSync信號排隊而導致處理延遲,這就是設計的問題了,這也是為什么Android文檔里反復強調(diào)在Activity的onXXX()里不要做太耗時的工作,因為這些回調(diào)函數(shù)和Choreographer運行在同一個線程里,這個線程就是所謂的UI線程。 言歸正傳,繼續(xù)往前,VSync事件最終在doFrame()里調(diào)了三次doCallbacks()來完成不同的功能, 分別處理用戶輸入事件,動畫刷新(動畫就是定時更新的圖片), 最后執(zhí)行performTraversals(),這個函數(shù)里面主要是檢查當前窗口當前狀態(tài),比如說是否依然可見,尺寸,方向,布局是否發(fā)生改變(可能是由前面的用戶輸入觸發(fā)的),分別調(diào)用performMeasure(), performLayout, performDraw()完成測量,布局和繪制工作。我們會在后面詳細學習這三個函數(shù),這里我們主要看一下第一次進入performTraversals的情況,因為第一次會做些初始化的工作,最重要的一件就是如本節(jié)標題,創(chuàng)建Surface對象。 回看圖2,我們可以看到Surface的創(chuàng)建不是在Activity進程里,而是在WindowManagerService完成的(粉顏色)。當一個Activity第一次顯示的時候,Android顯示切換動畫,因此Surface是在動畫的準備過程中創(chuàng)建的,具體發(fā)生在類WindowStateAnimator的createSurfaced()函數(shù)。它最終創(chuàng)建了一個SurfaceControl 對象。SurfaceControl是Android 4.3 里新引進的類,Google從之前的Surface類里拆出部分接口,變成SurfaceControl,為什么要這樣? 為了讓結構更清晰,WindowManagerService 只能對Surface進行控制,但并不更新Surface里的內(nèi)容,分拆之后,WindowManagerService 只能訪問SurfaceControl,它主要控制Surface的創(chuàng)建,銷毀,Z-order,透明度,顯示或隱藏,等等。而真正的更新者,View會通過Canvas的接口將內(nèi)容畫到Surface上。那View怎么拿到WMService創(chuàng)建的Surface,答案是下面的代碼里,surfaceControl 被轉換成一個Surface對象,然后傳回給ViewRoot, 前面創(chuàng)建的空的Surface現(xiàn)在有了實質內(nèi)容。Surface通過這種方式被創(chuàng)建出來,Surface對應的Buffer 也相應的在SurfaceFlinger內(nèi)部通過HAL層模塊(GRAlloc)分配并維護在SurfaceFlinger 內(nèi)部,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創(chuàng)建。接下來我們具體看一下View是如何畫在Surface Buffer上的,而Surface Buffer的顯示則交由圖解Android - Android GUI 系統(tǒng) (3) - Surface Flinger 來討論。 3. View的Measure, Layout 和 Draw直接從前面提到的performMeasure()函數(shù)開始. 因為遞歸調(diào)用,實際的函數(shù)調(diào)用棧比這里顯示的深得很多,這個函數(shù)會從view的結構樹頂(DecorView), 一直遍歷到葉節(jié)點。中間會經(jīng)過三個基類,DecorView, ViewGroup 和 View, 它們的類結構如下圖所示: 所有可見的View(不包括DecorView 和 ViewGroup)都是一個矩形,Measure的目的就是算出這個矩形的尺寸, mMeasuredWidth 和 mMeasuredHeight (注意,這不是最終在屏幕上顯示的尺寸),這兩個尺寸的計算受其父View的尺寸和類型限制,這些信息存放在 MeasureSpec里。MeasureSpec 里定義了三種constraints, widthMeasureSpec 和 heightMeasureSpec 作為 onMeasure的參數(shù)出入,子View根據(jù)這兩個值計算出自己的尺寸,最終調(diào)用 setMeasuredDimension() 更新mMeasuredWidth 和 mMeasuredHeight. performMeasure() 結束后,所有的View都更新了自己的尺寸,接下來進入performLayout(). performLayout() 的流程和performMeasure基本上一樣,可以將上面圖中的measure() 和 onMeasure 簡單的換成 layout() 和 onLayout(), 也是遍歷整課View樹,根據(jù)之前算出的大小將每個View的位置信息計算出來。這里不做太多描述,我們把重心放到performDraw(), 因為這塊最復雜,也是最為重要的一塊。 很早就玩過Android手機的同學應該能體會到Android2.3 到 Android 4.0 (其實Android3.0就有了,只是這個版本只在平板上有)的性能的巨大提升,UI界面的滑動效果一下變得順滑很多,到底是framework的什么改動帶來的?我們馬上揭曉。。。(這塊非本人工作領域,網(wǎng)上相關的資料也很少,所以純憑個人磚研,歡迎拍磚指正) Android Graphics Hardware AccelerationOK, 如標題所述,最根本的原因就是引入了硬件加速, GPU是專門優(yōu)化圖形繪制的硬件單元,很多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的實現(xiàn)了。 在此之前,簡單的介紹一下OpenGL的一些概念,否則很難理解。要想深入理解Opengl,請必讀經(jīng)典的紅包書:http://www./red/ Opengl說白了,就是一組圖形繪制的API。 這些API都是一些非?;镜拿睿ㄟ^它,你可以構造出非常復雜的圖形和動畫,同時,它又是跟硬件細節(jié)無關的,所以無需改動就可以運行在不同的硬件平臺上(前提是硬件支持所需特性)。OpenGL的輸入是最基本幾何元素(geometric primitives), 點(points), 線(lines), 多邊形(polygons), 以及bitmap和pixle data, 他的輸出是一個或兩個Framebuffer(真3D立體). 輸入到輸出的流程(rendering pipeline)如下圖所示: 這里有太多的概念,我們只描述跟本文相關的幾個: vertex data Triangles OpenGL只能畫非凹(nonconvex)的多邊形,可是現(xiàn)實世界中存在太多的凹性的物體,怎么辦呢?通過連線可以將凹的物體分成若干個三角形,三角形永遠都是凸(convex)的。同時三角形還有一個特性,三個點可以唯一確定一個平面,所以用盡可能多的三角形就可以逼近現(xiàn)實世界中復雜的曲線表面,比如下圖的例子,三角形的數(shù)目越多,球體的表示就越逼真。這也是為什么我們經(jīng)常看到顯卡的性能評測都以三角形的生成和處理作為一個非常重要的指標。 Display List 所有的Vertex和Pixel信息均可以存在Display List 里面,用于后續(xù)處理,換句話說,Display List 就是OpenGL命令的緩存。Display List的使用對OpenGL的性能提升有很大幫助。這個很容易理解,想象一個復雜的物體,需要大量的OpenGL命令來描繪,如果畫一次都需要重新調(diào)用OpenGL API,并把它轉換成Vertex data,顯然是很低效的,如果把他們緩存在Display List里,需要重繪的時候,發(fā)一個個命令通知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數(shù)據(jù)(比如Fonts)。通常是以矩陣的形式存放在內(nèi)存當中。通過Pxiel data, 我們避免大量的圖形繪制命令。同時通過現(xiàn)實世界中獲取的紋理圖片,可以將最終的物體渲染得更逼真。比如說畫一堵墻,如果沒有pixel data,我們需要將每塊磚頭都畫出來,也就是說需要大量的Vertex。可是如果通過一張現(xiàn)實生活中拍攝的磚墻的圖片,只需要4個點畫出一個大矩形,然后上面貼上紋理,顯然,速度和效果都要好得多。 FrameBuffer Framebuffer就是Opengl用來存儲結果的buffer。Opengl的frameBuffer類型有幾種。Front Buffer 和 Back Buffer, 分別用于顯示和繪制,兩者通過swapBuffer 進行交換。Left Buffer 和 Right buffer, 用于真立體(需要帶眼鏡的那種) 圖像的左右眼Buffer,Stencil buffer, 用于禁止在某些區(qū)域上進行繪制,想像一下如果在一件T恤上印上圖案,你是不是需要一個鏤空的紙板?這個紙板就是stencil buffer. OK, 對Opengl rendering pipeline簡單介紹到此,有興趣的同學可以閱讀opengl的紅包書或運行一些簡單的例子來深入理解Opengl?;氐街黝},僅僅使用Opengl 和 GPU 取代Skia 就能夠大幅提升性能?答案當然不是,性能的優(yōu)化很大程度上取決于應用,應用必須正確的使用Opengl命令才能發(fā)揮其最大效能。Android從pipeline 角度提供了兩種機制來提升性能,一個就是我們剛才說到的Display List,另一個叫 Hardware Layer, 其實就是緩存的FrameBuffer, 比如說Android的墻紙,一般來說,他是不會發(fā)生變化的,因此我們可以將它緩存在Hardware Layer里,這張就不需要每次進行拷貝和重繪,從而大幅提升性能。 說白了,優(yōu)化圖形性能的核心在于 1)用硬件來減少CPU的參與,加速圖形計算。 2)從軟件角度,通過Display List 和 Hardware Layer, 將已經(jīng)完成的工作盡可能的緩存起來,只做必須要做的事情,盡可能的減少運算量。 接下來看實現(xiàn)吧。 Canvas, Renderer, DisplayList, HardwareLayer 實現(xiàn)這塊代碼相當?shù)膹碗s,花了兩天時間才把下面的圖整理出來,但還是沒有把細節(jié)完全吃透,簡單的介紹一下框架和流程吧,如果有需要大家可以下來細看代碼。 圖中上半部為Java 代碼,下半部為Native層。先介紹里面出現(xiàn)的一些概念: Canvas Canvas是Java層獨有的概念,它為View提供了大部分圖形繪制的接口。這個類主要用于純軟件的繪制,硬件加速的圖形繪制則由HardwareCanvas取代。 HardwareCanvas,GLES20Canvas, GLES20RecordingCanvas hardwareCanvas是一個抽象類,如果系統(tǒng)屬性和應用程序指定使用硬件加速(現(xiàn)已成為默認),它將會被View(通過AttachInfo,如 下圖所示) 引用來完成所有的圖形繪制工作。GLES20Canvas 則是hardwareCanvas的實現(xiàn),但它也只是一層封裝而已,真正的實現(xiàn)在Native 層,通過jni (andriod_view_gles20Canvas.cpp)接口來訪問底層的Renderer, 進而執(zhí)行OpenGL的命令。 此外,GLES20Canvas還提供了一些靜態(tài)接口,用于創(chuàng)建各類Renderer對象。 GLES20RecordingCanvas 繼承GLES20Canvas, 通過它調(diào)用的OpenGL命令將會存儲在DisplayList里面,而不會立即執(zhí)行。 HardwareRenderer, GLRender, GL20Renderer 這三個類都是Java的Wrapper類,通過訪問各種Canvas來控制繪制流程。詳見下面兩張時序圖。 OpenGLRenderer, DisplayListRenderer, HardwareLayerRenderer Java的HardwareCanvas 和 HardwareRenderer在底層的對應實現(xiàn)。OpenGLRenderer是基類,只有它直接訪問底層OpenGL庫。DisplayListRenderer 將View通過GLES20Canvas傳過來的OpenGL 命令存在OpenGL的DisplayList中。而HardwareLayerRenderer 管理HardwareLayer的資源。 GLES20 EGL View, Canvas, Renderer, DisplayList, HardwareLayer 的關系如下圖所示: 每個View都對應一個DisplayList, 在Native層代碼里管理。每個View通過GLESRecordingCanvas 以及Native層對應的DisplayRenderer 將OpenGL命令存入DisplayList.最后View 通過GLES20Canvas 通知OpenGLRenderer 執(zhí)行這些DisplayList 里面的OpenGL 命令。 他們的生命周期如下圖所示 (粉紅代表 New, 黑色代表 Delete, 黃色代表Java類,藍色代表C++, 綠色代表JNI).
等等!好像少了點什么,怎么沒有DisplayList? 前面不是說它是性能優(yōu)化的幫手之一嗎?對了,上面只介紹了繪制的開始和結尾,在View的生命周期中,還有最重要的一步,Draw 還沒有被介紹,DisplayList 相關的操作就是在Draw()里面完成的。 Draw 流程繞了好大一圈,終于回到最初的話題,Android是怎樣將View畫出來的? 讓我們按照圖中的序號一一進行講解。(黃色:Java, 綠色:C++,藍色:JNI,粉色:New, 黑色:Delete).
Hardware Layer即便是使用了DisplayList, 對于復雜的圖形,仍然需要執(zhí)行大量的OpenGL命令,如果需要對這一部分進行優(yōu)化,就需要使用到 HardwareLayer對繪制的圖形進行緩存,如果圖形不發(fā)生任何變化,就不需要執(zhí)行任何OpenGL命令,而是將之前緩存在GPU內(nèi)存的Buffer 直接與其他View進行合成,從而大大的提高性能。這些存儲在GPU內(nèi)部的Buffer就稱為 Hardware Layer。除了Hardware Layer, Android 還支持Software Layer,和Hardware Layer 不同之處在于,它不存在于GPU內(nèi)部,而是存在CPU的內(nèi)存里,因此它不經(jīng)過前面所說的 Hardware Render Pipeline, 而是走Android最初的軟件Render pipeline。不管是Hardware Layer 還是 Software Layer, 在Draw() 內(nèi)部均稱為Cache,只要有Cache的存在,相對應的View將不用重繪,而是使用已有的Cache。Hardware Layer, Software Layer 和 Display List 是互斥的,同時只能有一種方法生效(當然,Hardware Layer的第一次繪制還是通過Display List 完成),下表總結了它們的差別, 從中可以看到,Hardware Layer 對性能的提升是最大的,唯一的問題是占用GPU的內(nèi)存(這也是為什么顯卡的內(nèi)存變得越來越大的原因之一),所以一般來說,Hardware Layer使用在那些圖片較為復雜,但不經(jīng)常改變,有動畫操作或與其他窗口有合成的場景,比如說WallPaper, Animation 等等。
重繪 - Invaliate前面介紹了View的第一次繪制的過程。但是一個View在運行中終究是要發(fā)生變化的,比如說,用戶在TextView的文字發(fā)生了改變,或者動畫導致View的尺寸發(fā)生變化,再或者說一個對話框彈出然后又消失,被遮擋的部分重新露了出來,這些都需要對View進行重繪。在介紹重繪之前,我們先了解一下View內(nèi)部的一些Flag 定義。
從上表可以看出,View通過內(nèi)部這些Flag來控制重繪?;旧现乩L分兩種情況,一種是需要重新生成DisplayList, 另外一種是使用之前已有的Cache,包括DisplayList或者是Hardware Layer。使用哪種重繪方式由當前View的Flags,以及應用程序傳入的參數(shù)決定??刂扑木褪且唤MInvalidate() 函數(shù)。
void invalidate(boolean invalidateCache) { if (skipInvalidate()) { //如果View不可見,并且不再動畫退出過程中(fade out),將不執(zhí)行Invalidate(). return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || //DRAWN -> 已經(jīng)被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,則有系統(tǒng)根據(jù)Dirty Region決定是否需要重新生成DisplayList。 } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { //系統(tǒng)不支持Dirty Region,必須重繪整個區(qū)域, 基本不會進去 } 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. } } } 假如所有的條件都支持重繪,便會調(diào)用到ViewParent的invalidateChild()方法。(ViewParent是一個接口類,它的實現(xiàn)類是ViewGroup 和 ViewRootImpl。)這個方法會從當前View開始,向上遍歷到ViewRoot 或者 到某個ViewGroup的區(qū)域與當前View的Dirty區(qū)域沒有重疊為止。途中的每個ViewGroup都會被標記上Dirty。在接下來VSYNC的performDraw()里,ViewRootImpl 會遍歷所有標記Dirty的ViewGroup,然后找到里面標記Dirty的View,只有這些View的DisplayList 被重建,而其他實際上沒有變化的View(雖然它們在同一個ViewGroup里面),如果沒有Hardware Layer, 只需重新執(zhí)行對應Display List 里面的OpenGL 命令。通過這種方式,Android只重繪需要重繪的View,從軟件層面將GPU的輸入最小化,從而優(yōu)化圖形性能。
4. Windows 的管理到此,我們已經(jīng)了解了一個Acitivty(Window)是如何畫出來的,讓我們在簡要重溫一下這個過程:
注意的是,上面討論的只是一個窗口的流程,而Android是個多窗口的系統(tǒng),窗口之間可能會有重疊,窗口切換會有動畫產(chǎn)生,窗口的顯示和隱藏都有可能會導致資源的分配和釋放,這一切需要有一個全局的服務進行統(tǒng)一的管理,這個服務就是我們大名鼎鼎的Window Manager Service (簡寫 WMS). 其實Window Manager Service 的工作不僅僅是管理窗口,還會跟很多其他服務打交道,如 InputManager Service, AcitivityManager Service 等等,但本章只討論它在Window Manager 方面的工作,下圖中紅色標記部分。 Layout首先來看Layout。Layout 是Window Manager Service 重要工作之一,它的流程如下圖所示:
Android里定義了很多區(qū)域,如下圖所示
Overscan: OverscanScreen, Screen: Restricted and Unrestricted: mFrame, mDisplayFrame, mContainingFrame mContentFrame, mVisibleFrame Insects Layout 在WMS 內(nèi)部的時序如下圖所示,外部調(diào)整Overscan參數(shù)或View內(nèi)部主動調(diào)用requestLayout() 都會觸發(fā)WMS的重新layout,layout完成后,WMS會通過IWindow的resized()接口通知ViewRoot, 最終會調(diào)用requestLayout(), 并在下一個VSYNC 事件到來時更新。
計算Layout主要有圖中三個紅色的函數(shù)完成,它們代碼很多,涉及到很多計算,但只要對著我們上面給的三個圖來看,不難看出它的意思,本文將不詳細深入。 AnimationAnimation的原理很簡單,就是定時重繪圖形。下面的類圖中給出了Android跟Animation相關的類。 Animation: Animator: WindowStateAnimator, WindowAnimator, AppWindowAnimator: 具體來看一下Window的Animation和View的Animation
View 的動畫實現(xiàn)步驟與Windows 類似,有興趣的同學可以去看View.java 的 drawAnimation() 函數(shù)。 管理窗口WMS 里面管理著各式各樣的窗口, 如下表所示(在WindowManagerService.java 中定義)
可以看到這里大量的用到了隊列,不同的窗口,或同一窗口在不同的階段,可能會出現(xiàn)在不同的隊列里。另外因為WindowManager Service 的服務可能被很多個線程同時調(diào)用,在這種復雜的多線程環(huán)境里,通過鎖來實現(xiàn)線程安全非常難以實現(xiàn),一不小心就可能導致死鎖,所以在 WindowManager 內(nèi)專門有一個執(zhí)行線程(WM Thread)來將所有的服務請求通過消息進行異步處理,實現(xiàn)調(diào)用的序列化。隊列是實現(xiàn)異步處理的常用手段。隊列加Looper線程是Android 應用常用的設計模型。 此外,WindowManager還根據(jù)Window的類型進行了分類(在WindowManager.java),如下表,
windowManager Service 會根據(jù)窗口的類型值來決定Z-Order (于常量值無關,值大說明是后面Android版本添加的,比如說2025~2028就是4.3 新加的)。比如說SurfaceView.java 里的一個函數(shù), 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 內(nèi)部轉換成幾個Z-Order 值,mBaseLayer, mSubLayer, mAnimationLayer, 分別表明主窗口,子窗口(附加在主窗口之上),和動畫窗口的Z-Order值(越大越在上邊)。不同的窗口類型在不同的硬件產(chǎn)品上有不同的定義,因此它是實現(xiàn)在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, 所以,我們?nèi)绻谑謾C鎖屏時顯示歌曲播放進度,就必須給這個窗口分配一個大于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" 可以查看系統(tǒng)當前運行的窗口的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 是通過調(diào)整窗口的mViewVisibility 和 mLayer 值來實現(xiàn)窗口重疊。最后給出跟Z-order相關的類圖。
圖中序號表示輸入法窗口找到它的目標窗口的過程:
WindowManager Service的介紹暫告一段落,它與其他重要的Service,SurfaceFlinger, ActivityManager, InputManager, PowerManager, WatchDog 之間的關系將在其他文章介紹。
|
|
來自: 老匹夫 > 《Graphics》