目錄介紹
- 3.0.0.1 View的繪製需要經過哪些過程?有哪些常用回撥方法?View的繪製流程的詳細流程是怎樣的?
- 3.0.0.2 View繪製流程,當一個TextView的例項呼叫setText()方法後執行了什麼?請說一下原理……
- 3.0.0.3 requestLayout()、invalidate()與postInvalidate()有什麼區別?
- 3.0.0.4 DecorView的作用是什麼?DecorView中如何獲取ContentView以及Activity所設定的View?ViewRootIml如何和DecorView建立聯絡?
- 3.0.0.5 getWidth()方法和getMeasureWidth()區別呢?為什麼有時候用getWidth()或者getMeasureWidth()得到0?
- 3.0.0.6 平時寫的自定義控制元件有哪些?如何優化自定義view?View的繪製流程說一下?自定義View的注意點?
- 3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的處理邏輯?
- 3.0.0.8 ViewGroup(抽象類)的measure流程?getChildMeasureSpec獲取子元素MeasureSpec的要點?
- 3.0.0.9 View的layout過程?View的layout()原始碼分析?LinearLayout的onLayout方法?View的測量寬高和最終寬高有什麼區別?
- 3.0.1.0 draw的過程步驟是什麼?View特殊方法setWillNotDraw是幹什麼用的?
- 3.0.1.1 View中x,y,translationX,translationY分別是什麼?View平移時是否改變了left、top等原始引數?
- 3.0.1.2 MeasureSpec是什麼?MeasureSpec的組成?測量模式SpecMode的型別和具體含義?MeasureSpec和LayoutParams的對應關係?
- 3.0.1.3 如何獲取View的測量寬/高?如何在Activity啟動時獲得View的寬/高?Activity中獲得View寬高的4種辦法?
- 3.0.1.4 Activity啟動到最終載入ViewRoot(執行三大流程)的流程是什麼?
- 3.0.1.5 自定義View效能優化有哪些?針對異常銷燬,自定義View如何優化?如何避免建立大量物件?
好訊息
- 部落格筆記大彙總【15年10月到至今】,包括Java基礎及深入知識點,Android技術部落格,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的檔案是markdown格式的!同時也開源了生活部落格,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
- 連結地址:github.com/yangchong21…
- 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!所有的筆記將會更新到GitHub上,同時保持更新,歡迎同行提出或者push不同的看法或者筆記!
3.0.0.1 View的繪製需要經過哪些過程?有哪些常用回撥方法?View的繪製流程的詳細流程是怎樣的?
- View的繪製需要經過哪些過程?
- measure:測量View的寬和高
- View的measure方法是final型別方法——表明該方法無法被過載
- View的measure方法會呼叫onMeasure方法,onMeasure會呼叫setMeasuredDimension方法設定View寬/高的測量值
- layout:確定View在父控制元件中的放置位置
- draw:負責將View繪製在螢幕上。技術部落格大總結
- measure:測量View的寬和高
- 有哪些常用回撥方法?
- 構造方法
- onAttachToWindow:在包含View的Activity啟動時呼叫
- onDetachFromWindow:在包含View的Activity退出或者View被remove時回撥
- onVisibilityChanged:當View的可見狀態發生改變時呼叫
- 比較重要的概念
- ViewRoot:連線WindowManager(外界訪問Window的入口)和DecorView(頂級View)的紐帶,View的三大流程均是通過ViewRoot來完成的。
- DecorView:頂級View
- DecorView是頂級View,本質就是一個FrameLayout
- 包含了兩個部分,標題欄和內容欄
- 內容欄id是content,也就是activity中setContentView所設定的部分,最終將佈局新增到id為content的FrameLayout中
- View的繪製流程的詳細流程是怎樣的?技術部落格大總結
- View的繪製流程是從ViewRoot的PerformTraversals方法開始的。大概的流程如下所示
- performTraversals會依次呼叫performMeasure, performLayout, performDraw三個方法,這三個方法分別完成頂層View的measure,layout,draw方法,onMeasure又會呼叫所有子元素的measure過程,直到完成整個View樹的遍歷。同理,performLayout, performDraw的傳遞流程與performMeasure相似。唯一不同在於,performDraw的傳遞過程在draw方法中通過dispatchDraw實現,但沒有本質區別。
- Measure過程後可以呼叫getMeasureWidth和getMeasureHeight方法獲取View測量後的寬高,與getWidth和getHeight的區別是:getMeasuredHeight()返回的是原始測量高度,與螢幕無關,getHeight()返回的是在螢幕上顯示的高度。實際上在當螢幕可以包裹內容的時候,他們的值是相等的,只有當view超出螢幕後,才能看出他們的區別。當超出螢幕後,getMeasuredHeight()等於getHeight()加上螢幕之外沒有顯示的高度。
- Layout過程確定View四個頂點的位置和實際的寬高。
- Draw過程確定View的顯示,只有draw方法完成後View的內容才會出現在螢幕上。
- View的繪製流程是從ViewRoot的PerformTraversals方法開始的。大概的流程如下所示
3.0.0.2 View繪製流程,當一個TextView的例項呼叫setText()方法後執行了什麼?請說一下原理……
- View的繪製流程主要分為三步:
- onMeasure:測量檢視的大小,從頂層父View到子View遞迴呼叫measure()方法,measure()呼叫onMeasure()方法,onMeasure()方法完成繪製工作。
- onLayout:確定檢視的位置,從頂層父View到子View遞迴呼叫layout()方法,父View將上一步measure()方法得到的子View的佈局大小和佈局引數,將子View放在合適的位置上。
- onDraw:繪製最終的檢視,首先ViewRoot建立一個Canvas物件,然後呼叫onDraw()方法進行繪製。onDraw()方法的繪製流程為
- ① 繪製檢視背景。
- ② 繪製畫布的圖層。 技術部落格大總結
- ③ 繪製View內容。
- ④ 繪製子檢視,如果有的話。
- ⑤ 還原圖層。
- ⑥ 繪製滾動條。
3.0.0.3 requestLayout()、invalidate()與postInvalidate()有什麼區別?requestLayout()何時不會觸發onDraw()?
- invalidate() postInvalidate()
- invalidate()該方法遞迴呼叫父View的invalidateChildInParent()方法,直到呼叫ViewRootImpl的invalidateChildInParent()方法,最終觸發ViewRootImpl的performTraversals()方法,此時mLayoutRequestede為false,不會觸發onMesaure()與onLayout()方法,有可能會觸發onDraw()方法。
- 共同點:都是呼叫onDraw()方法,然後去達到重繪view的目的
- 區別:invalidate()用於主執行緒,postInvalidate()用於子執行緒
- requestLayout()
- 該方法會遞迴呼叫父視窗的requestLayout()方法,直到觸發ViewRootImpl的performTraversals()方法,此時mLayoutRequestede為true,會觸發onMesaure()與onLayout()方法,不一定會觸發onDraw()方法。
- 當view確定自身已經不再適合現有的區域時,該view本身呼叫這個方法要求parent view(父類的檢視)重新呼叫他的onMeasure、onLayout來重新設定自己位置。特別是當view的layoutparameter發生改變,並且它的值還沒能應用到view上時,這時候適合呼叫這個方法requestLayout()。requestLayout呼叫onMeasure和onLayout,不一定呼叫onDraw技術部落格大總結
- 如何選擇
- 一般說來需要重新佈局就呼叫requestLayout()方法,需要重新繪製就呼叫invalidate()方法。
- requestLayout()何時不會觸發onDraw()?
- 如果沒有改變控制元件的left\right\top\bottom就不會觸發onDraw()
- invalidate()在什麼情況下不會觸發onDraw?
- 在ViewGroup中,invalidate預設不重新繪製子view。
- 如何讓ViewGroup在invalidate時會觸發onDraw?技術部落格大總結
- 本質需要將ViewGroup的dirtyOpaque設定為false
- 1.在建構函式中呼叫setWillNotDraw(false);
- 2.給ViewGroup設定背景。呼叫setBackground。
- 本質需要將ViewGroup的dirtyOpaque設定為false
3.0.0.4 DecorView的作用是什麼?DecorView中如何獲取ContentView以及Activity所設定的View?ViewRootIml如何和DecorView建立聯絡?
- DecorView的作用是什麼?
- DecorView是頂級View,本質就是一個FrameLayout
- 包含了兩個部分,標題欄和內容欄,內容欄id是content,也就是activity中setContentView所設定的部分,最終將佈局新增到id為content的FrameLayout中技術部落格大總結
- DecorView中如何獲取ContentView以及Activity所設定的View?
- 獲取content:ViewGroup content = findViewById(R.android.id.content)
- 獲取設定的View:content.getChidlAt(0)
- ViewRootIml如何和DecorView建立聯絡?技術部落格大總結
- Activity物件在ActivityThread中建立完畢後,會將DecorView新增到Window中
- 同時會建立ViewRootImpl,呼叫ViewRoot的setView方法將ViewRootImpl和DevorView建立關聯
root = new ViewRootImpl(view.getContext(), display); root.setView(view, wparams, panelParentView); 複製程式碼
- ViewRoot為什麼要和DecorView建立關聯
- DecorView等View的三大流程需要通過ViewRoot完成
3.0.0.5 getWidth()方法和getMeasureWidth()區別呢?為什麼有時候用getWidth()或者getMeasureWidth()得到0?
- getWidth()方法和getMeasureWidth()區別呢
- getMeasureWidth()
- getMeasureWidth()方法在measure()過程結束後就可以獲取到了,另外,getMeasureWidth()方法中的值是通過setMeasuredDimension()方法來進行設定的
- 這裡mMeasuredWidth & MEASURED_SIZE_MASK表示的是測量階段結束之後,view真實的值。
public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; } 複製程式碼
- getWidth()
- getWidth()方法要在layout()過程結束後才能獲取到,getWidth()方法中的值則是通過檢視右邊的座標減去左邊的座標計算出來的。
- mRight和mLeft是什麼值,是在什麼時候被設定的。具體看layout()過程中原始碼
@ViewDebug.ExportedProperty(category = "layout") public final int getWidth() { return mRight - mLeft; } 複製程式碼
- getMeasureWidth()
- 為什麼有時候用getWidth()或者getMeasureWidth()得到0
- 問題描述:使用getMeasuredWidth()和getMeasuredHeight()方法,無論是在onCreate()、onStart()、onResume()中呼叫,都無法得到控制元件的長度、和寬度。如下圖,測量的結果為0。
- 解釋:技術部落格大總結
- 因為View的Measure過程和Activity的生命週期方法不是同步執行的,所以無法保證Activity執行了onCreate()、onStart()、onResume()時某個View已經測量完畢了,如果View還沒有測量完畢,那麼獲得寬/高就是0。
- 後來覺得這種解釋有點牽強,比如
- 解決控制元件測量寬高問題
- 如下所示
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); test(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); getWidth(4); } private void test(){ getWidth(1); marqueeView.measure(0, 0); getWidth(2); marqueeView.post(new Runnable() { @Override public void run() { getWidth(3); } }); } private void getWidth(int a){ int width2 = marqueeView.getWidth(); int measuredWidth2 = marqueeView.getMeasuredWidth(); Log.e(a+"MainActivity-----",width2+"-----"+measuredWidth2); } //11-28 17:03:17.559 15990-15990/com.yc.cn.ycbanner E/1MainActivity-----: 0-----0 //11-28 17:03:17.567 15990-15990/com.yc.cn.ycbanner E/2MainActivity-----: 0-----760 //11-28 17:03:17.684 15990-15990/com.yc.cn.ycbanner E/3MainActivity-----: 960-----960 //11-28 17:03:17.685 15990-15990/com.yc.cn.ycbanner E/4MainActivity-----: 960-----960 複製程式碼
- 什麼時候測量寬高不等於實際寬高?
- MeasuredWidth/height!=getWidth/Height()的場景:更改View的佈局引數並進行重新佈局後,就會導致測量寬高!=實際寬高
3.0.0.6 平時寫的自定義控制元件有哪些?如何優化自定義view?View的繪製流程說一下?自定義View的注意點?
- 平時寫的自定義控制元件有哪些?
- 1組合控制元件。這種自定義控制元件不需要我們自己繪製,而是使用原生控制元件組合成的新控制元件。如標題欄,recyclerView封裝控制元件。
- 2繼承原有的控制元件。這種自定義控制元件在原生控制元件提供的方法外,可以自己新增一些方法。比如圖片縮放控制元件,進度條控制元件。
- 3完全自定義控制元件:這個View上所展現的內容全部都是我們自己繪製出來的。比如百分比進度條控制元件
- 如何優化自定義 view
- 為了加速你的view,對於頻繁呼叫的方法,需要儘量減少不必要的程式碼。先從onDraw開始,需要特別注意不應該在這裡做記憶體分配的事情,因為它會導致GC,從而導致卡頓。在初始化或者動畫間隙期間做分配記憶體的動作。不要在動畫正在執行的時候做記憶體分配的事情。
- 你還需要儘可能的減少onDraw被呼叫的次數,大多數時候導致onDraw都是因為呼叫了invalidate().因此請儘量減少呼叫invaildate()的次數。如果可能的話,儘量呼叫含有4個引數的invalidate()方法而不是沒有引數的invalidate()。沒有引數的invalidate會強制重繪整個view。技術部落格大總結
- 另外一個非常耗時的操作是請求layout。任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有衝突的值,它會需要重新計算好幾次。另外需要儘量保持View的層級是扁平化的,這樣對提高效率很有幫助。
- 如果你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操作。與內建的view不同,自定義的view可以使得程式僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展示瞭如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據他自身的layout法則,直接設定它們的大小。
- View的繪製流程說一下?
- View的繪製流程:OnMeasure()——>OnLayout()——>OnDraw()
- 第一步:OnMeasure():測量檢視大小。從頂層父View到子View遞迴呼叫measure方法,measure方法又回撥OnMeasure。
- 第二步:OnLayout():確定View位置,進行頁面佈局。從頂層父View向子View的遞迴呼叫view.layout方法的過程,即父View根據上一步measure子View所得到的佈局大小和佈局引數,將子View放在合適的位置上。
- 第三步:OnDraw():繪製檢視。ViewRoot建立一個Canvas物件,然後呼叫OnDraw()。六個步驟:①、繪製檢視的背景;②、儲存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子檢視,如果沒有就不用;⑤、還原圖層(Layer);⑥、繪製滾動條。技術部落格大總結
- 自定義View的注意點?
- View需要支援wrap_content、padding
- ViewGroup需要支援子View的margin和自身的padding
- 儘量不要在View中使用Handler,View已經有post系列方法
- View如果有執行緒或者動畫,需要及時停止(onDetachedFromWindow會在View被remove時呼叫)——避免記憶體洩露
- View如果有滑動巢狀情形,需要處理好滑動衝突
3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的處理邏輯?
- View的wrap_content和match_parent效果一致的原因分析?
- 根據View的onMeasure方法中的getDefaultSize方法,我們可以發現在兩種模式下,View的測量值等於該View的測量規格MeasureSpec中的尺寸。
- View的MeasureSpec本質是由自身的LayoutParams和父容器的MeasureSpec決定的。
- 當View為wrap_content時,該View的模式為AT_MOST,且尺寸specSize為父容器的剩餘空間大小。
- 當View為match_parent時,該View的模式跟隨父容器的模式(AT_MOST/EXACTLY), 且尺寸specSize為父容器的剩餘空間大小。
- 因此getDefaultSize中無論View是哪種模式,最終測量寬/高均等於尺寸specSize,因此兩種屬性效果是完全一樣的(View的大小充滿了父容器的剩餘空間)技術部落格大總結
- 除非給定View固定的寬/高,View的specSize才會等於該固定值。
- getDefaultSize方法的處理邏輯?
- getDefaultSize: 根據建議獲取的最小寬高和測量規格,決定實際的測量寬高
- UNSPECIFIED模式:測量寬高 = 建議的最小寬高
- EXACTLY / AT_MOST模式:測量寬高 = specSize技術部落格大總結
- View的getDefaultSize原始碼要點(決定了View寬高的測量值)
- UNSPECIFIED模式時,寬/高為第一個引數也就是getSuggestedMinimumWidth()獲取的建議最小值
- AT_MOST(wrap_content)和EXACTLY(match_parent/具體值dp等)這兩個模式下,View寬高的測量值為當前View的MeasureSpec(測量規格)中指定的尺寸specsize
- getDefaultSize: 根據建議獲取的最小寬高和測量規格,決定實際的測量寬高
3.0.0.8 ViewGroup(抽象類)的measure流程?getChildMeasureSpec獲取子元素MeasureSpec的要點?
- ViewGroup(抽象類)的measure流程?
- ViewGroup沒有onMeasure方法,只定義了measureChildren方法(onMeasure根據不同佈局難以統一)
- measureChildren中遍歷所有子元素並呼叫measureChild方法
- measureChild方法中會獲取子View的MeasureSpec(getChildMeasureSpec),然後呼叫子元素View的measure方法進行測量
- getChildMeasureSpec獲取子元素MeasureSpec的要點?
- 子View的MeasureSpec是根據自身的LayoutParams和父容器SpecMode生成
- 當子View的佈局引數為wrap_content,且父容器模式為AT_MOST時,效果與子元素佈局為match_parent是一樣的。因此當子View的佈局引數為wrap_content時,需要給指定預設的寬/高
- LinearLayout的onMeasure()分析
- ViewGroup因為佈局的不同,無法統一onMeasure方法,具體內容根據佈局的不同而不同,這裡直接以LinearLayout進行分析
- onMeasure會根據orientation選擇measureVertical或者measureHorizontal進行測量
- measureVertical本質是遍歷子元素,並執行子元素的measure方法,並獲得子元素的總高度以及子元素在豎直方向上的margin等。技術部落格大總結
- 最終LinearLayout會測量自己的大小,在orientation的方向上,如果佈局是match_parent或者具體數值,測量過程與View一致(高度為specSize);如果佈局是wrap_content,高度是所有子元素高度總和,且不會超過父容器的剩餘空間,最終高度需要考慮在豎直方向上的padding
3.0.0.9 View的layout過程?View的layout()原始碼分析?LinearLayout的onLayout方法?View的測量寬高和最終寬高有什麼區別?
- View的layout過程?
- 使用layout方法確定View本身的位置
- layout中呼叫onLayout方法確定所有子View的位置
- View的layout()原始碼分析?
- 呼叫setFrame()設定View四個定點位置(即初始化mLeft,mRight,mTop,mBottom的值)
- 之後呼叫onLayout確定子View位置,該方法類似於onMeasure,View和ViewGroup中均沒有實現,具體實現與具體佈局有關。
- LinearLayout的onLayout方法?
- 根據orientation選擇呼叫layoutVertical或者layoutHorizontal
- layoutVertical中會遍歷所有子元素並呼叫setChildFrame(裡面直接呼叫子元素的layout方法)
- 層層傳遞下去完成了整個View樹的layout過程
- setChildFrame中的寬/高實際就是子元素的測量寬/高(getMeasure…後直接傳入)
- View的測量寬高和最終寬高有什麼區別?技術部落格大總結
- 等價於getMeasuredWidth和getWidth有什麼區別
- getWidth = mRight - mLeft,結合原始碼測量值和最終值是完全相等的。
- 區別在於:測量寬高形成於measure過程,最終寬高形成於layout過程(賦值時機不同)
- 也有可能導致兩者不一致:強行重寫View的layout方法,在傳參方面改變最終寬/高(雖然這樣毫無實際意義)
- 某些情況下,View需要多次measure才能確定自己的測量寬高,在前幾次測量中等到的值可能有最終寬高不一致。但是最終結果上,測量寬高=最終寬高
3.0.1.0 draw的過程步驟是什麼?View特殊方法setWillNotDraw是幹什麼用的?
- draw的過程步驟是什麼?
- 繪製背景(drawBackground(canvas))
- 繪製自己(onDraw)
- 繪製children(dispatchDraw)-遍歷呼叫所有子View的draw方法
- 繪製裝飾(如onDrawScollBars)
- View特殊方法setWillNotDraw是幹什麼用的?
- 若一個View不繪製任何內容,需要將該標誌置為true,系統會進行相應優化
- 預設View不開啟該標誌位技術部落格大總結
- 預設ViewGroup開啟該標誌位
- 如果我們自定義控制元件繼承自ViewGroup並且本身不進行繪製時,就可以開啟該標誌位
- 當該ViewGroup明確通過onDraw繪製內容時,就需要顯式關閉WILL_NOT_DRAW標誌位。
3.0.1.1 View中x,y,translationX,translationY分別是什麼?View平移時是否改變了left、top等原始引數?
- View中x,y,translationX,translationY分別是什麼?
- x,y是View當前左上角的座標
- translationX,translationY是在滑動/動畫後,View當前位置和View最原始位置的距離。
- 因此得出等式:x(View左上角當前位置) = left(View左上角初始位置) + translationX(View左上角偏移的距離)
- View平移時是否改變了left、top等原始引數?技術部落格大總結
- View平移時top、left等引數不變,改變的是x,y,tranlsationX和tranlsationY
3.0.1.2 MeasureSpec是什麼?MeasureSpec的組成?測量模式SpecMode的型別和具體含義?MeasureSpec和LayoutParams的對應關係?
- MeasureSpec是什麼?
- MeasureSpec是一種“測量規則”或者“測量說明書”,決定了View的測量過程
- View的MeasureSpec會根據自身的LayoutParamse和父容器的MeasureSpec生成。
- 最終根據View的MeasureSpec測量出View的寬/高(測量時資料並非最終寬高)
- MeasureSpec的組成?
- MeasureSpec代表一個32位int值,高2位是SpecMode,低30位是SpecSize
- SpecMode是指測量模式
- SpecSize是指在某種測量模式下的大小
- 類MesaureSpec提供了用於SpecMode和SpecSize打包和解包的方法
- 測量模式SpecMode的型別和具體含義?技術部落格大總結
- UNSPECIFIED:父容器不對View有任何限制,一般用於系統內部
- EXACTLY:精準模式,View的最終大小就是SpecSize指定的值(對應於LayoutParams的match_parent和具體的數值)
- AT_MOST:最大值模式,大小不能大於父容器指定的值SpecSize(對應於wrap_content)
- MeasureSpec和LayoutParams的對應關係?
- View的MeasureSpec是需要通過自身的LayoutParams和父容器的MeasureSpec一起才能決定
- DecorView(頂級View)是例外,其本身MeasureSpec由視窗尺寸和自身LayoutParams共同決定
- MeasureSpec一旦確定,onMeasure中就可以確定View的測量寬/高
3.0.1.3 如何獲取View的測量寬/高?如何在Activity啟動時獲得View的寬/高?Activity中獲得View寬高的4種辦法?
- 如何獲取View的測量寬/高?
- 在measure完成後,可以通過getMeasuredWidth/Height()方法,就能獲得View的測量寬高
- 在一定極端情況下,系統需要多次measure,因此得到的值可能不準確,最好的辦法是在onLayout方法中獲得測量寬/高或者最終寬/高
- 如何在Activity啟動時獲得View的寬/高?
- Activity的生命週期與View的measure不是同步執行,因此在onCreate/onStart/onResume均無法正確得到
- 若在View沒有測量好時,去獲得寬高,會導致最終結果為0
- 有四種辦法去正確獲得寬高
- Activity中獲得View寬高的4種辦法?技術部落格大總結
- onWindowFocusChanged
- View已經初始化完畢,可以獲得寬高;Activity得到焦點和失去焦點均會呼叫一次(頻繁onResume和onPause會導致頻繁呼叫)
- view.post(runnable)
- 通過post將一個runnable投遞到訊息佇列尾部;等到Looper呼叫次runnable時,View已經完成初始化
- ViewTreeObserver
- 使用ViewTreeObserver的介面,可以在View樹狀態改變或者View樹內部View的可見性改變時,onGlobalLayout會被回撥;能正確獲取View寬/高
- view.measure
- onWindowFocusChanged
3.0.1.4 Activity啟動到最終載入ViewRoot(執行三大流程)的流程是什麼?
- Activity啟動到最終載入ViewRoot(執行三大流程)的流程是什麼?
- Activity呼叫startActivity方法,最終會呼叫ActivityThread的handleLaunchActivity方法
- handleLaunchActivity會呼叫performLauchActivity方法(會呼叫Activity的onCreate,並完成DecorView的建立)和handleResumeActivity方法
- handleResumeActivity方法會做四件事:performResumeActivity(呼叫activity的onResume方法)、getDecorView(獲取DecorView)、getWindowManager(獲取WindowManager)、WindowManager.addView(decor, 1)
- WindowManager.addView(decor, 1)本質是呼叫WindowManagerGlobal的addView方法。其中主要做兩件事:1、建立ViewRootImpl例項 2、root.setView(decor, ….)將DecorView作為引數新增到ViewRoot中,這樣就將DecorView載入到了Window中
- ViewRootImpl還有一個方法performTraveals方法,用於讓ViewTree開始View的工作流程:其中會呼叫performMeasure/Layout/Draw()三個方法,分別對應於View的三大流程。
3.0.1.5 自定義View效能優化有哪些?針對異常銷燬,自定義View如何優化?如何避免建立大量物件?
- 自定義View效能優化有哪些?
- 避免過度繪製
- 儘量減少或簡化計算
- 避免建立大量物件造成頻繁GC
- 禁止或避免I/O操作
- onDraw中避免冗餘程式碼、避免建立物件
- 複合View,要減少佈局層級。
- 狀態和恢復和儲存
- 開啟硬體加速
- 合理使用invalidate的引數版本。
- 減少冗餘程式碼:不要使用Handler,因為已經有post系列方法.
- 使用的執行緒和動畫,要在onDetachedFromWindow中進行清理工作。
- 要妥善處理滑動衝突。
- 避免過度繪製
- 畫素點能畫一次就不要多次繪製,以及繪製看不到的背景
- 開發者選項裡內的工具,只對xml佈局有效果,看不到自定義View的過度繪製,仍然需要注意。
- 儘量減少或簡化計算
- 不要做無用計算。儘可能的複用計算結果。技術部落格大總結
- 沒有資料,或者資料較少的時候應如何處理,沒有事件需要響應的時候如何處理。
- 應該避免在for或while迴圈中做計算。比如:去計算螢幕寬度等資訊。
- 避免建立大量物件造成頻繁GC
- 應該避免在for或while迴圈中new物件。這是減少記憶體佔用量的有效方法。
- 禁止或避免I/O操作
- I/O操作對效能損耗極大,不要在自定義View中做IO操作。
- onDraw中避免冗餘程式碼、避免建立物件
- onDraw中禁止new物件.如:不應該在ondraw中建立Paint物件。Paint類提供了reset方法。可以在初始化View時建立物件。
- 要避免冗餘程式碼,提高效率。
- 複合View,要減少佈局層級。
- 複合控制元件:繼承自現有的LinearLayout等ViewGroup,然後組合多個控制元件來實現效果。這種實現方法要注意減少佈局層級,層級越高效能越差。
- 狀態和恢復和儲存
- Activity還會因為記憶體不足或者旋轉螢幕而導致重建Activity,自定義View也要去進行自我狀態的儲存和讀取。
- 在onSaveInstanceState()儲存狀態;在onRestoreInstanceState()恢復狀態
- 開啟硬體加速
- 合理使用invalidate的引數版本。技術部落格大總結
- 避免任何請款下之際呼叫預設引數的invalidate
- 呼叫有引數的invalidate進行區域性和子View重新整理,能夠提高效能。
- 減少冗餘程式碼:不要使用Handler,因為已經有post系列方法
- View已經有post系列方法,沒有必要重複去寫。
- 可以直接使用,最終會投遞到主執行緒的Handler中
- 使用的執行緒和動畫,要在onDetachedFromWindow中進行清理工作。
- View如果有執行緒或者動畫,需要及時停止.
- View的onDetachedFromWindow會在View被remove時呼叫,在該方法內進行終止
- 這樣能避免記憶體洩露
- 要妥善處理滑動衝突。
- View如果有滑動巢狀情形,需要處理好滑動衝突
關於其他內容介紹
01.關於部落格彙總連結
02.關於我的部落格
- 我的個人站點:www.yczbj.org, www.ycbjie.cn
- github:github.com/yangchong21…
- 知乎:www.zhihu.com/people/yczb…
- 簡書:www.jianshu.com/u/b7b2c6ed9…
- csdn:my.csdn.net/m0_37700275
- 喜馬拉雅聽書:www.ximalaya.com/zhubo/71989…
- 開源中國:my.oschina.net/zbj1618/blo…
- 泡在網上的日子:www.jcodecraeer.com/member/cont…
- 郵箱:yangchong211@163.com
- 阿里雲部落格:yq.aliyun.com/users/artic… 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:segmentfault.com/u/xiangjian…
- 掘金:juejin.im/user/593943…