一個Activity的顯示

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對象

Created with Raphaël 2.1.0WindowManagerImplWindowManagerImplViewRootViewRootWindowManagerServiceWindowManagerServiceSessionSessionnew ViewRoot()new Surface()openSession()new Session()add()new SurfaceSession()relayout()relayoutWindow()Surface surface = win.createSurfaceLocked();outSurface.copyFrom(surface);

(待續)

3. 畫圖四大組件

  • Bitmap
  • Canvas
  • Drawing primitive
  • Paint

4. FrameBuffer

圖像數據的傳輸過程是:

  • 由客戶端把數據寫到共享內存中
  • 然後由SurfaceFlinger從共享內存中取出數據再往硬件發送

承載圖像數據的就是FrameBuffer(簡稱FB)

FrameBuffer的中文名叫緩衝幀,它實際上包含兩方面:

  • Frame:幀,指一幅圖像,即屏幕上的一幀。
  • Buffer:緩衝,就是一段內存,用來存儲幀數據。

FrameBuffer中的Buffer,是通過mmap系統調用把顯示設備中的顯存映射到用戶空間,在這塊緩衝區上寫數據,就相當於在屏幕上繪畫。

Linux FrameBuffer(簡稱LFB),是Linux平臺提供的一種可直接操作FB的機制,依託這個機制,應用層通過標準的系統調用,就可以直接操作顯示設備了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章