invalidate()是用來刷新View的,必須是在UI線程中進行工作。比如在修改某個view的顯示時,調用invalidate()才能看到重新繪制的界面。invalidate()的調用是把之前的舊的view從主UI線程隊列中pop掉。 一個Android 程序默認情況下也只有一個進程,但一個進程下卻可以有許多個線程。
在這么多線程當中,把主要是負責控制UI界面的顯示、更新和控件交互的線程稱為UI線程,由于onCreate()方法是由UI線程執行的,所以也可以把UI線程理解為主線程。其余的線程可以理解為工作者線程。
invalidate()得在UI線程中被調動,在工作者線程中可以通過Handler來通知UI線程進行界面更新。
Android提供了Invalidate方法實現界面刷新,但是Invalidate不能直接在線程中調用,因為他是違背了單線程模型:Android UI操作并不是線程安全的,并且這些操作必須在UI線程中調用。
簡單說invalidate是在ui線程中使用的。相應的,postinvalidate是在非ui線程里面使用的比如說你用handler刷新,不開新的線程,用invalidate就行了。如果你加載了另外一個線程,就要用postinvalidate。
主要原因是Activity的ui刷新基于單線程模型,非ui線程上的ui操作是不允許的,會報錯。所以在非ui線程上用postinvalidate給ui線程一個訊號,讓ui線程進行刷新操作
使用postInvalidate比較簡單,不需要handler,直接在線程中調用postInvalidate即可,除了onCreate()不是運行在UI線程上的,其實其他大部分方法都是運行在UI線程上的,其實只要你沒有開啟新的線程,你的代碼基本上都運行在UI線程上
再來看postInvalidateOnAnimation的執行過程
InvalidateOnAnimationRunnable本質上是一個Runnable ,run()方法中實際執行view.invalidate方法,也就是說現在的問題是run()怎么執行的,什么時候執行的?!
這里將InvalidateOnAnimationRunnable 做為參數傳給了mChoreographer對象,也就是這里的邏輯就完成了,剩下的invalidate任務交給了mChoreographer。
其中mPosted為true時表示已經通知了mChoreographer來處理刷新的任務,但任務還沒有真正執行,也就在有新的view刷新任務添加時,不再重復通知了。
c.run(frameTimeNanos);這里也就是對應InvalidateOnAnimationRunnable的run()方法。
c是CallbackRecord的實例,CallbackRecord里擁有InvalidateOnAnimationRunnable的實例,即下面代碼中的action;
c.run(frameTimeNanos);中有個時間參數,但((Runnable)action).run();是沒有用到的,可以不關心。
所以現在找定義執行該函數時間的地方。
doCallbacks被doFrame方法調用,doFrame方法執行是在handleMessage中:
而發送MSG_DO_FRAME消息的代碼為:
那么執行時間就由nextFrameTime來決定了。也就是從當前時間開始的10ms內(理論情況)。
這里是沒有使用Vsync同步機制的情況,使用Vsync會增加一些另外的操作,但是最終執行邏輯是一樣的,詳細的分析可參考Android系統Choreographer機制實現過程。
總結一下:
postInvalidate方法是將任務添加到隊列中排隊后立即執行的,而postInvalidateOnAnimation依賴上一幀動畫的的執行時間,因為動畫的刷新是存在一個頻率的,直到下一幀動畫的時間才會真正執行刷新操作。
而postIfNeededLocked()干的事情就是把mInvalidateOnAnimationRunnable作為Choreographer.CALLBACK_ANIMATION(這個類型的task會在mesaure/layout/draw之前被運行)的Task 交給 UI線程的Choreographer.
View的invalidate會進一步觸發ViewRootImpl的invalidateChildInParent()->invalidate()<一種情況(dirty == null 表示全部重繪),不過另外一種差不多>:
不過雖然沒有發出去,不代表這次invalidate就不會生效,因為前面的invalidate()里已經設置了mDirty了: