Android界面繪制

Android界面繪制整體框圖

任何一個(gè)操作系統(tǒng)實(shí)現(xiàn)界面繪制,都需要處理應(yīng)用層、系統(tǒng)層和硬件層的分工協(xié)作:

  • 應(yīng)用層負(fù)責(zé)定義畫面的內(nèi)容
  • 系統(tǒng)層負(fù)責(zé)綜合整個(gè)屏幕的畫面并保證流暢
  • 硬件層負(fù)責(zé)把數(shù)據(jù)輸出到顯示設(shè)備上
應(yīng)用層

除了系統(tǒng)窗口(Toast),主要在Activity中繪制界面,需要解決兩個(gè)問題

  • 定義顯示內(nèi)容?;驹砭褪窃贑anvas上繪制界面,然后調(diào)用surfaceholder.unlockCanvasAndPost函數(shù),渲染到Surface中(視頻是解碼出視頻幀,渲染到Surface上),Surface實(shí)際處于系統(tǒng)層,通過Ashmem共享內(nèi)存?zhèn)鹘oActivity使用。
  • 定義顯示位置、層次和生命周期?;驹砭褪茿ctivity的PhoneWindow利用Bindler機(jī)制和系統(tǒng)層通信,交給系統(tǒng)層去統(tǒng)一管理,如addView、removeView等都是通過WMS去做的。
系統(tǒng)Framework層

對(duì)于應(yīng)用開發(fā)來說,最重要的是系統(tǒng)層中的Framework層,主要包括WMS和SurfaceFlinger兩個(gè)系統(tǒng)服務(wù),都運(yùn)行在SystemServer進(jìn)程中:

  • WindowManagerService,負(fù)責(zé)兩件事:Window的層級(jí),Window的管理。
    層級(jí)上,WMS把所有界面分為應(yīng)用window,子window和系統(tǒng)window三種,分別有自己的層級(jí)范圍(1 - ~,1000 - ~, 2000 - ~).
    管理上,WMS要負(fù)責(zé)添加和刪除window,管理這些window的的位置、大小和生命周期變化。
    另外,WMS在調(diào)整window的時(shí)候,喲啊通知SurfaceFlinger去更新界面,由此用戶才能看到調(diào)整后的效果。

  • SurfaceFlinger,負(fù)責(zé)兩件事:為應(yīng)用提供Surface,整合圖形數(shù)據(jù):

    • Activity獲取Surface時(shí),是WMS代為向SurfaceFlinger做的請(qǐng)求
    • 根據(jù)WMS的窗口層級(jí),把相關(guān)的Surface整合起來,并放到BufferQueue里,供底層繪制界面,實(shí)際上起到了生產(chǎn)者的作用。
系統(tǒng)HAL層,系統(tǒng)Linux Kernel層和硬件層

把系統(tǒng)層的這兩部分和硬件層放在一起說,是因?yàn)樗麄兟?lián)系更緊密,更偏底層,平時(shí)做應(yīng)用開發(fā)時(shí)也基本不涉及到。
HAL層:是個(gè)抽象接口,處理界面的是Gralloc接口,HAL是為了解決linux硬件驅(qū)動(dòng)的版權(quán)問題(Android開源,但是有些廠商的硬件驅(qū)動(dòng)不開源,用HAL可以規(guī)避這些問題)。
Linux Kernel層:Linux 內(nèi)核使用幀緩沖FrameBuffer來實(shí)現(xiàn)顯示功能,作為內(nèi)存緩沖區(qū)(有32個(gè)Slot),既是操作硬件設(shè)備的接口,又可以緩解畫面流暢和完整性的問題(隊(duì)列、生產(chǎn)和消費(fèi))。
硬件層:利用驅(qū)動(dòng)把數(shù)據(jù)輸出到顯示設(shè)備上。

Activity與Framework層的合作

Activity需要與Framework層的SurfaceFlinger和WMS合作,才能實(shí)現(xiàn)界面繪制,這個(gè)合作機(jī)制需要解決這樣幾個(gè)問題:

  • Window如何創(chuàng)建與使用
  • Surface如何獲取與使用
  • View如何創(chuàng)建與繪制

Window的創(chuàng)建與使用

Window的管理核心在WMS,所以Window的創(chuàng)建和使用都需要與WMS建立通信,并交給WMS管理和調(diào)度。

  • 創(chuàng)建:Activity啟動(dòng)時(shí)創(chuàng)建PhoneWindow,并與WMS建立通信,以便統(tǒng)一管理。
  • 使用: 在ViewRootImpl中調(diào)用WMS的addToDisplay,實(shí)現(xiàn)添加窗口。

具體流程如下:

  • 主線程ActivityThread啟動(dòng)Activity時(shí),調(diào)用的performLaunchActivity會(huì)執(zhí)行activity的attach函數(shù)關(guān)聯(lián)context,application等,這時(shí)就會(huì)創(chuàng)建PhoneWindow,并把PhoneWindow和WMS關(guān)聯(lián),還會(huì)給WMS提供反向訪問的Bindler參數(shù)mToken。
  • PhoneWindow持有一個(gè)WindowManagerImpl實(shí)例,WindowManagerImpl中有一個(gè)WindowManagerGolbal的靜態(tài)變量,是一個(gè)單例。
  • 在WindowManagerGlobal中執(zhí)行addView的操作(ActivityThread的handleResumeActivity方法中會(huì)調(diào)用WindowManagerGlobal的addView方法,將DecorView傳入,并在方法內(nèi)部實(shí)例化一個(gè)ViewRootImpl實(shí)例,將其作為DecorView的ParentView(ViewTree的繪制都是從ViewRootImpl開始的),ViewRootImpl的setView方法中,會(huì)調(diào)用WMS的addToDisplay方法,實(shí)現(xiàn)添加這個(gè)窗口)。
框圖

所以,App內(nèi)部的所有窗口由WindowManagerGlobal統(tǒng)一管理,而android系統(tǒng)的所有窗口由WMS統(tǒng)一管理。

Surface的獲取與使用

Surface是ViewRootImpl通過Bindler機(jī)制從SurfaceFlinger中通過Ashmem共享內(nèi)存獲取到的。
實(shí)際上,ViewRootImpl持有一個(gè)Surface對(duì)象,所以問題在于,ViewRootImpl中如何為Surface關(guān)聯(lián)到了SurfaceFlinger中的對(duì)象。
具體流程如下:

  • 在ViewRootImpl中不管是setView方法還是requestLayout方法,最后都會(huì)調(diào)用scheduleTraversals方法,并在TraversalRunnable中執(zhí)行performTraversals方法(這里就是ViewTree繪制的起點(diǎn));在performTraversals方法中首先會(huì)調(diào)用relayoutWindow方法。relayoutWindow方法內(nèi)部會(huì)調(diào)用mWindowSession.relayout,進(jìn)行Binder通信。
  • WMS會(huì)先創(chuàng)建一個(gè)SurfaceControl,然后利用copyFrom獲取其中的Surface。這里會(huì)執(zhí)行native函數(shù)nativeCreateFromSurfaceControl。最后,native層會(huì)通過SurfaceComposerClient去訪問SurfaceFlinger,SurfaceFlinger從BufferQueue中dequeueBuffer,最終返回Surface。
框圖

View的創(chuàng)建與繪制前獲取Canvas

我們定義的ViewTree其實(shí)是DecorView中R.id.content那一部分,所以View的創(chuàng)建與繪制,核心在于建立與Window的關(guān)聯(lián),并能訪問Surface。

具體流程如下:

  • View是從Activity(實(shí)際是PhoneWindow)的setContentView創(chuàng)建的(xml資源轉(zhuǎn)換為ViewTree)
  • 然后DecorView和PhoneWindow相互關(guān)聯(lián)。并且DecorView關(guān)聯(lián)了ViewRootImpl對(duì)象,ViewRootImpl會(huì)幫忙DecorView完成繪制的工作。

總結(jié)

除了Activity之外,最重要的就是ViewRootImpl、PhoneWindow和WindowManagerGlobal。

  • ViewRootImpl是繪制的起點(diǎn)(控制DectorView的繪制),也是繪制的目標(biāo)(mSurface),每次WindowManagerGlobal中addView,都會(huì)生成并保存一個(gè)ViewRootImpl對(duì)象;最終的繪制canvas,也是渲染到ViewRootImpl持有的mSurface中去。
  • PhoneWindow夾在Activity和DecorView之間,主要起到解耦和減負(fù)的作用,可以把Activity與View的管理/window的管理切割開。
    例如,添加View實(shí)際上是交給WindowManager去addView/removeView/updateView,但是Activity不需要直接與WindowManager交互,而是讓PhoneWindow去setContentView,PhoneWindow再去調(diào)用WindowManager的addView操作。
  • WindowManager是個(gè)抽象類,只有WindowManagerImpl一個(gè)實(shí)現(xiàn),而WindowManagerImpl實(shí)際上。
    ViewRootImpl被WindowManagerGlobal嚴(yán)密地管理了起來,WMS管理window時(shí),也是通過操縱ViewRootImpl實(shí)現(xiàn)的,所以都說ViewRootImpl是WindowManager和DecorView的連接紐帶。

擴(kuò)展

場(chǎng)景1

自定義系統(tǒng)開機(jī)畫面,雖然沒有啟動(dòng)Android,但是可以操作硬件驅(qū)動(dòng)、或操作linux的framebuffer實(shí)現(xiàn)界面繪制,這也是各廠商自己定制系統(tǒng)時(shí)的修改方法。

場(chǎng)景2

側(cè)滑App,為什么可以向一側(cè)滑動(dòng)整個(gè)App界面,考慮到App實(shí)際上是向DecorView添加了ViewTree,這就可以在DecorView和ViewGroup中間插一層透明的View,這樣就能滑動(dòng)原有的ViewTree,達(dá)到側(cè)滑效果。

public class SwapeBackLayout extends FrameLayout{
     ...
     //用當(dāng)前ViewGroup代替ViewTree的根節(jié)點(diǎn)
     ViewGroup decorView= (ViewGroup) activity.getWindow().getDecorView();
     View child=decorView.getChildAt(0);
     decorView.removeView(child);
     addView(child);
     decorView.addView(this);
     ...
}
場(chǎng)景3

我們知道事件分發(fā)是從Activity開始的,但是懸浮窗也可以設(shè)置為允許響應(yīng)事件,但是懸浮窗是沒有Activity的,只做了addView,那么懸浮窗的事件是如何響應(yīng)的?
硬件層攔截到事件,會(huì)從WMS傳遞到ViewRootImpl,而ViewRootImpl是WindowManagerGlobal在addView時(shí)創(chuàng)建的,所以懸浮窗雖然只做了addView,也有ViewRootImpl,也能響應(yīng)事件。
有Activity的情況下,ViewRootImpl的mView是DecorView,所以會(huì)把事件傳遞給DecorView,DecorView處理事件的函數(shù)為:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

其中,mWindow是持有DecorView的PhoneWindow對(duì)象,而這個(gè)PhoneWindow對(duì)象的Callback,是在Activity的attach中,創(chuàng)建出PhoneWindow后,把Activity作為了Callback。

//Activity源碼
    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) {
        ...
        mWindow = new PhoneWindow(this, window);
        ...
        mWindow.setCallback(this);

所以,在有Activity的情況下,事件傳遞是ViewRootImpl-->DectorView-->PhoneWindow.Callback也就是Activity,然后再傳遞給PhoneWindow-->DectorView-->ViewTree,這也可以解釋為什么DectorView里同時(shí)存在dispatchTouchEvent和superDispatchTouchEvent兩個(gè)函數(shù)。
而在沒有Activity的情況下,ViewRootImpl的mView是我們addView時(shí)傳入的ViewTree,事件就直接傳遞給ViewTree了。

參考

從系統(tǒng)角度理解Android的界面繪制

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容