編輯:關於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函數來處理。
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 (int widthMeasureSpec, int heightMeasureSpec) { } onLayout (boolean changed,int left, int top,int rig , ht, int bottom) { } onDraw (Canvas canvas) { }
下面的鏈接有比較好的說明,先不再贅述。
上面說了View最終都會在ViewRootImpl.requestLayout()函數中請求把View的內容畫到Surface上。
ViewRootImpl裡邊有一個成員變量mSurface就是保存Surface對象的,這個成員變量是通過WindowManagerService來申請的。
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
其實上面的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是擁有自己畫圖空間的唯一的View。SurfaceView的存在是為了克服View畫圖的時間點是由ViewRoot決定的短板。SurfaceView在調用onDraw()的時候,會在自己需要畫的位置保留一塊透明的所謂punch hole。真正畫這塊內容是SurfaceView會利用SurfaceHolder和其他輔助線程。
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等。
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);
待續..
最近由於個人興趣原因,寫了個模仿新聞頂部導航標簽的demo。具體看下圖。那麼大致上我們會用到這些知識。1.Fragment2.FragmentPagerAdapter3.
1.前言隨著項目的快速迭代,往往上一個版本正在測試中,下一個版本已經開始開發了和版本追溯了。目前移動客戶端還沒有版本管理控制上形成一套有效的管理體系,希望能通過此文檔的整
仿QQ消息列表item橫向滑動刪除ListView中item側滑刪除在最近的項目中,我的ListView中item選項是長按刪除的效果(Android的通常做法長按或點擊
前言因為在之前的一些項目用到圖表的次數較多,如果由自己一步步來畫的話,目前的水平和效果或者完成不了需求。而且現在網上的關於圖表的“輪子”也是比較豐