Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android : Activity,Window and View.

Android : Activity,Window and View.

編輯:關於Android編程

看一下Activity是怎麼通過View,Window等來用於自己的顯示的。

這裡寫圖片描述
上圖是Activity的Lifecycle。
這裡只想說一下按back鍵和按home鍵退出,重新打開一個activity的時候的流程。
第一次冷啟動流程是:onCreate()->onStart()->onResume()
按Home鍵退出時:onPause()->onStop()
重新打開時:onStart()->onResume()
按Back鍵退出時:onPause()->onStop()->onDestroy()
重新打開時:onCreate()->onStart()->onResume()

1.entire lifetime : 這個生命周期發生在第一調用onCreate(Bundle)方法到最終的對onDestroy()的調用。一個Acitivity可以在onCreate()方法中對全局的狀態進行初始化,在onDestroy中釋放所有用到的資源。例如:如果需要一個後台運行的從網絡中下載數據的線程,那麼可以在onCreate()函數中進行創建,在onDestroy()中停止。

2.visible lifetime: 可視期介於調用 onStart 與 onStop 之間。此時Activity是可見的,但是不能響應用戶事件。一個Activity在其生命周期中是有可能經歷多個可視期的。在非常極端的情況下,系統也有可能終止一個處於可視期的活動,這種情況很少見。onStop 方法通常用於暫時或者停止那些用來更新UI的動畫,線程,定時器,服務等,所以當活動不可見的時占用的系統資源是很少的。當活動由不可見狀態轉化為可見狀態時,在 onStart 中再啟動相關的線程和服務。onStart 和 onStop 同樣也用來注冊和取消注冊(unregister)那些用來更新UI的廣播接收者。在活動不可見時我們需要取消注冊接受者(Receivers),特別是那些支持目的動作(Intent)以及更新UI的接收者。這也是為什麼在onCreate()中setContentView()之後界面不是馬上可以看到的原因!!!這個部分還需要看~~

3.foreground lifetime: 前台生命期從onResume()到onPause()。在這期間,這個activity在其他activity的前面,且正在與用戶進行交互。一個activity可以很頻繁的在Resumed與Paused狀態之間進行切換。例如當設備Sleep時、一個Activity result被傳遞時、一個新的Intent被傳遞時等。所以為了有良好的用戶體驗,在 onResume 和 onPause 方法中的代碼需要有更高的效率。foregroud的activity也可以在dumpsys meminfo裡看到。

Activity的創建過程:parseBaseApk()->parseBaseApplication()->parseActivity()->

private Activity parseActivity(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
            boolean receiver, boolean hardwareAccelerated)
            throws XmlPullParserException, IOException {
                 ...
                Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
                ...
           }

onResume()的過程:

android.app.ActivityThread.performResumeActivity()
->android.app.Activity.performResume()
->android.app.Instrumentation.callActivityOnResume()
->com.sec.android.app.camera.Camera.onResume()

上面這些Activity的初始化以及回調函數的注冊過程後面再看。

下面來看Activity和Window,View之間是什麼關系,以及怎麼利用View來畫自己的界面的。
一個Activity可以畫的部分,一般就是整個屏幕當中除了StatusBar以外的部分。
這裡寫圖片描述

一個Activity,可以有很多個View。這個可以在自己的res/layout目錄下看到



    
    

像這樣的一個Layout,會被組織成下面這樣階梯狀的Layout和View。
這裡寫圖片描述

這裡寫圖片描述

這裡的PRODUCT_POLICY是Android編譯的時候指定的。有PhoneWindow和MIDWindow policy兩種。一般都是用的PhoneWindow。
這個圖裡邊可以看到一個Activity會有一個Window(或者說是PhoneWindow,Window類是個抽象類,PhoneWindow是Window類的唯一的實現類)。
總結幾點就是:

1.Activity用自己Window,有人說肯定只有一個,也有人說可以有多個,有待確認。總之Activity.attach()函數會嗲用mWindow = new PhoneWindow(this)這樣創建一個PhoneWindow保存在Activity.mWindow中。

2.之後Activity.setContentView()在getWindow().setContentView()會調用的PhoneWindow的setConteView(),在這裡會檢查是否有DecorView。如果沒有就會調用installDecor()函數創建一個DecorView和mContentParent,然後把setContentView()傳入的View或者layoutID轉成View,都加到mContentParent當中(mLayoutInflater.inflate(layoutResID, mContentParent))。PhoneWindow都有一個叫DecorView的子類。DecorView又繼承自FrameLayout。FrameLayout又繼承自ViewGroup。ViewGroup繼承自View。
DecorView是Activity持有的最頂層的一個唯一的View(也可以說是上面圖的ViewGroup吧~)。PhoneWindow中的DecorView是整個界面包含ActioBar(statusbar除外),那layout裡邊用戶定義的view是PhoneWindow裡邊的mContentParent。之所以這樣應該是ActionBar和背景都是只畫一次的。(可以看installDecor()->generateLayout()函數)。
這裡寫圖片描述

3.DecorView創建完需要加到WindowManager裡的。這個過程可以看Activity裡的setVisible()函數。這個函數內部調用了makeVisible()函數把DecorView加到WindowManager中。

void makeVisible() {  
       if (!mWindowAdded) {  
           ViewManager wm = getWindowManager();  
           wm.addView(mDecor, getWindow().getAttributes());  
           mWindowAdded = true;  
       }  
       mDecor.setVisibility(View.VISIBLE);  
   }  

4.界面上的點擊等操作是由WindowManagerService接收消息,然後回調 Activity函數來處理。

什麼時間點觸發View畫的流程?

Activity創建完DecorView,會調用WindowManagerImpl.addView()。看上面makeVisible()函數。這最後會調用的ViewRootImpl.setView()函數。最終會調到requestLayout()函數等會調用scheduleTraversals()函數把TraversalRunnable注冊到Choreographer.postCallback上面。

mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

也就是說ViewRootImpl不會自己觸發畫Frame的過程,而是把它加到Choreographier的CallbackQueue中(從postCallback函數看下去就可以找到postCallbackDelayedInternal函數中有這樣的操作,但這裡又去的是postCallbackDelayedInternal函數還會判斷當前的Frame是不是已經超時,如果是的畫,就直接scheduleFrameLocked來調用doFrame()而不會把Frame加到CallbackQueue當中)。

這個流程可以看下面兩張圖,這兩張圖不准~~ 。
這裡寫圖片描述
這裡寫圖片描述
http://www.07net01.com/2015/07/866214.html

Choreographier的doFrame會回調ViewRootImpl的performMeasure(),perforLayout(),performDraw()等函數,一層一層調用view的相關的函數去畫好Surface。
這裡寫圖片描述
這個與上面說的畫的相符,就是ViewRoot開始,一層一層調用每個View的onDraw()函數把view的內容畫上去(Canvas也是ViewRoot創建傳下去的)。但onDraw()畫上去之後不會馬上從屏幕上看到,這個和Surfac,SurfaceFlinger相關。

那Choreographer的doFrame是什麼時間點開始的呢? doFrame是在Vsync信號來的時候調用。
Vsync信號是在FrameDisplayEventReceiver類繼承了DisplayEventReceiver類來進行注冊的,每次會注冊下一個Vsync來的時候回調函數onVsync()。
這個注冊過和回調的過程如下:
這裡寫圖片描述

http://www.csdn123.com/html/itweb/20131015/168825.htm

下面看看每個View的onMeasure,onLayout,onDraw函數說明

最後需要每個View負責把自己負責的正方型內的內容畫上去。當然要先測量自己的正方形大小,然後給視圖進行布局,也就是確定視圖的正確的位置。最後就是畫了。分別由下面的三個函數完成。

onMeasure (int widthMeasureSpec, int heightMeasureSpec) { }

onLayout (boolean changed,int left, int top,int rig , ht, int bottom) { }

onDraw (Canvas canvas) { }

下面的鏈接有比較好的說明,先不再贅述。

上面說的畫圖流程都是畫到Surface上面,那是怎麼得到Surface的?

上面說了View最終都會在ViewRootImpl.requestLayout()函數中請求把View的內容畫到Surface上。
ViewRootImpl裡邊有一個成員變量mSurface就是保存Surface對象的,這個成員變量是通過WindowManagerService來申請的。
這裡寫圖片描述

應用程序進程請求WindowManagerService服務為一個應用程序窗口創建一個Surface對象; WindowManagerService服務請求SurfaceFlinger服務創建一個Layer對象,並且獲得一個ISurface接口; WindowManagerService服務將獲得的ISurface接口保存在其內部的一個Surface對象中,並且將該ISurface接口返回給應用程序進程; 應用程序進程得到WindowManagerService服務返回的ISurface接口之後,再將其封裝成其內部的另外一個Surface對象中。
最終獲取Surface的地方在
WindowManagerService.relayoutWindow(....Surface outSurface) {
    ...
    SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
    outSurface.copyFrom(surfaceControl);//這個就是創建的Surface!!
    ... 
}

獲取到的Surface,當然就是會保存在ViewRootImpl.mSurface這裡了。

獲取完Surface之後,就可以利用Canvas去畫了。

final Canvas canvas;
canvas = mSurface.lockCanvas(dirty);//dirty區域
canvas.setHighContrastText(attachInfo.mHighContrastText);
...
mView.draw(canvas);//這裡會跑去調用每個View的onDraw()
...
surface.unlockCanvasAndPost(canvas);//交給RenderThread和OpenGL來畫BufferQueue

上面說已經用每層View的onDraw函數把Surface畫好了??

其實上面的surface.unlockCanvasAndPost(canvas);之前的內容也只是和Canvas相關,沒有實際畫到Surface上。或者說沒有畫到Surface的BufferQueue中。
真正用EGL處理Surface或者BufferQueue或者GraphicBuffer是在RenderThread裡處理。
在Systrace裡邊可以看到RenderThread的DrawFrame其實就是frameworks/base/libs/hwui/rederthread/DrawFrameTask.cpp的run()函數。
調用順序是:

Surface.unlockCanvasAndPost() -> Surface.unlockAndPost() -> nHwuiDraw(mHwuiRenderer) ->
android_view_Surface.cpp的draw() -> RenderProxy.cpp的syncAndDrawFrame()->DrawFrameTask.cpp的drawFrame()-> postAndWait()後面就是跑到run裡邊了

doFrame包含dequeueBuffer(), queueBuffer(),eglSwapBuffersWithDamageKHR()等流程。
doFrame完了之後,APP這邊的畫界面的流程就完成了,eglSwapBuffersWithDamageKHR()之後就會交給SurfaceFlinger去把該畫的內容給LCD了~~

在dequeueBuffer分配buffer時會調用到Gralloc.cpp的registerBuffer()函數。最終會跑到ionalloc.cpp的map_buffer函數最終用分配ion內存。

SurfaceView比較特殊!!!

SurfaceView是擁有自己畫圖空間的唯一的View。SurfaceView的存在是為了克服View畫圖的時間點是由ViewRoot決定的短板。SurfaceView在調用onDraw()的時候,會在自己需要畫的位置保留一塊透明的所謂punch hole。真正畫這塊內容是SurfaceView會利用SurfaceHolder和其他輔助線程。

Canvas

Canvas是對Bitmap或者Surface進行bit composition的。Canvas提供很多2D graphic API。這個不像OpenGL ES支持硬件加速。
Canvas使用Android 2D graphic library也就是skia。skia也是android的corecg,sgl,skiaagl等的基礎。這裡sgl是為了畫2D graphic, skiaagl之前是為了3D Open GL的,但好像現在不再使用。 sgl使用的外部library有gif,jpeg,png,freetyp library等。
這裡寫圖片描述

OpenGL ES - 3D Graphics Library

Android為了畫3D圖像使用OpenGL ES library。
EGL使用libagl.so(android GL)和libhgl.so(hardware GL)。下面是EGL Surface生成過程。

mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null);
GLSurface sur = eglCreateWindowSurface(dpy, cnf, new EGLNativeWindowSurface(window), base);
EGLNativeWindowSurface(const sp& surface); 

這裡寫圖片描述

activity viewroot window的關系

這裡寫圖片描述

SurfaceFlinger

這裡寫圖片描述

ActivityManager VS OOM

這裡寫圖片描述

待續..

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved