Activity的顯示過程主要經歷了Activity的Launch和Resume過程,下面針對其顯示過程中的一些重要步驟進行分析。
1. ActivityThread - performLaunchActivity
- 創建PhoneWindow
- 創建WindowManager
- setContentView
activity = mInstrumentation.newActivity(...);
activity.attach(...);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setWindowManager(...);
wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManager = new LocalWindowManager(wm); // PhoneWindow的WindowManager是一個LocalWindowManager裏面包含一個WindowManagerImpl
mWindowManager = mWindow.getWindowManager(); // activity也保存一份LocalWindowManager
mInstrumentation.callActivityOnCreate(activity, r.state);
activity.performCreate(state);
onCreate(state);
setContentView(view);
getWindow().setContentView(view);
setContentView(view, params);
installDecro();
mContentParent.addView(view, params); // mContentParent即android.R.id.content
2. ActivityThread - handleResumeActivity
- 創建ViewRoot並獲取WindowSession和W類
- 調用WindowSession.add -> WMS.addWindow,並將W類傳給WMS(RPC,SYNC)
- 調用ViewRoot的requestLayout(ASYNC)
wm = a.getWindowManager(); // 獲取activity的LocalWindowManager
wm.addView(decor, l);
mWindowManager.addView(view, params); // 這裏纔是WindowManagerImpl
root = new ViewRoot(view.getContext);
getWindowSession
// 將IBinder裝進一個IWindowManager.Proxy,Proxy也是一個IWindowManager
sWindowSession = IWindowManager.Stub.asInterface(ServiceManager.getService("window")).openSession(...);
mWindow = new W(this, context); // W是在這時創建的
root.setView(view, wparams, panelParentView);
mView = view; // 保存PhoneWindow中的DecorView
requestLayout(); // 最終異步調用到ViewRoot#performTraversals
res = sWindowSession.add(mWindow, ...); // 遠程調用WMS的addWindow
對於ViewRoot的理解:
按照平時寫佈局的直觀感覺,認爲一個View樹的根節點應該是一個ViewGroup,而實現上卻是一個ViewRoot,那麼這是爲什麼呢,這個就要看其實現的接口ViewParent的意義了。 ViewParent意爲一個View的父節點,那麼一個View的父節點理應是ViewGroup啊,其實不然,ViewGroup是一個ViewParent沒錯,而它本身也是一個View,那麼ViewGroup的作用就是要把自己顯示出來,並且把自己的子View也顯示出來 而ViewRoot只是一個ViewParent,其自身並不是一個View,那麼他就負責把子View顯示出來,而把忙着要顯示自己的精力留出來,處理一些與顯示相關的更重要的事情,比如與WMS打交道 這就猶如一個程序員要寫代碼,項目組長要管理程序員,同時自己也要寫代碼,而技術總監管理項目組長,自己卻不用寫代碼,把寫代碼的精力空出來去處理一些更重要的工作
經過上面的步驟,已經爲Activity添加了window,並調用requestLayout,進行了顯示,接下來就進一步分析Sessionr的添加window和ViewRoot的requelstLayout的過程。
2.1 Session - add
- 創建WindowState來表示一個Window
- 創建SurfaceSession與SurfaceFlinger建立連接
- 將應用端的Binder(即W類)傳給WMS,並在mWindowMap中保存W類和WindowState對應關係
WindowManagerService.addWindwow
win = new WindowState(...); // 用來表示一個Window
win.attach();
mSession.windowAddedLocked();
mSurfaceSession = new SurfaceSession(); // 在這裏終於與SurfaceFlinger扯上關係了
mNumWindow++;
mWindowMap.put(client.asBinder(), win); // 用map來存儲Binder和WindowState的對應關係
return res;
2.2 ViewRoot - requestLayout
relayoutWindow
// 這裏的mSurface相當於一個傳出參數,接收遠程WMS創建的Surface
// 遠端通過SurfaceSession創建一個Surface,則該Surface是已綁定一段與顯示相關的內存的,往該內存裏寫數據即可顯示在屏幕上
// 遠端創建的Surface的mNativeObject是一個SurfaceControl類型,將其通過parcel傳送過來,而傳到本地後,根據parcel構建出的是一個Surface類型
sWindowSession.relayout(..., mSurface);
draw
// 調用上面得到的surface的lockCanvas,可以得到一個綁定了其內存的canvas
// 調用mView(即DecorView)的draw函數,並將canvas傳入進去,即可將DecorView及下各層級子view繪製在該canvas所綁定的內存中並顯示在屏幕上
Surface surface = mSurface;
canvas = surface.lockCanvas(dirty);
mView.draw(canvas); // mView爲DecorView
surface.unlockCanvasAndPost(canvas);
2.2.1 Session - relayout
- 在服務端創建Surface,並傳回給應用端
WindowManagerService.relayoutWindow
Surface surface = win.createSurfaceLocked(); // win爲addWindow時創建的WindowState
mSurface = new Surface(mSession.mSurfaceSession, ...);
outSurface.copyFrom(surface); // 將Service端創建的surface傳給Client端
2.2.2 Surface - lockCanvas
- 從Surface中獲取SurfaceInfo,其包含綁定的內存,將該內存綁定到一個bitmap並賦給canvas
lockCanvasNative -> Surface_lockCanvas
// 這裏先取出Native的Surface
const sp<Surface>& surface(getSurface(env, clazz));
// 調用native的surface的lock並獲取SurfaceInfo
Surface::SurfaceInfo info;
status_t err = surface->lock(&info, &dirtyRegion);
// 獲取canvas和nativeCanvas
jobject canvas = env->GetObjectField(clazz, so.canvas);
SkCanvas* nativeCanvas = (SkCanvas*) env.GetIntField(canvas, no.native_canvas);
// 設置format,width,height,size和內存
bitmap.setConfig(...);
bit.setPixels(info.bits);
// 給canvas設置bitmap;
nativeCanvas.setBitmapDevice(bitmap);
2.3 ViewRoot和WMS關係
ViewRoot通過 IWindowManager.Stub.asInterface(ServiceManager.getService("window"))
獲得一個IWindowManager,然後通過其openSession獲得一個IWindowSession
ViewRoot通過IWindowSession遠程調用WMS
WMS通過IWindow遠程調用ViewRoot中的W對象
(待續)
3. 畫圖四大組件
- Bitmap
- Canvas
- Drawing primitive
- Paint
4. FrameBuffer
圖像數據的傳輸過程是:
- 由客戶端把數據寫到共享內存中
- 然後由SurfaceFlinger從共享內存中取出數據再往硬件發送
承載圖像數據的就是FrameBuffer(簡稱FB)
FrameBuffer的中文名叫緩衝幀,它實際上包含兩方面:
- Frame:幀,指一幅圖像,即屏幕上的一幀。
- Buffer:緩衝,就是一段內存,用來存儲幀數據。
FrameBuffer中的Buffer,是通過mmap系統調用把顯示設備中的顯存映射到用戶空間,在這塊緩衝區上寫數據,就相當於在屏幕上繪畫。
Linux FrameBuffer(簡稱LFB),是Linux平臺提供的一種可直接操作FB的機制,依託這個機制,應用層通過標準的系統調用,就可以直接操作顯示設備了。