在項目中遇到一個bug,程序在android1.6上直接crash,可是在其餘版本上均正常,錯誤日誌以下:html
04-07 17:02:53.512: E/AndroidRuntime(360): java.lang.RuntimeException: mBaselineAlignedChildIndex of LinearLayout set to an index that is out of bounds. 04-07 17:02:53.512: E/AndroidRuntime(360): at android.widget.LinearLayout.getBaseline(LinearLayout.java:151) 04-07 17:02:53.512: E/AndroidRuntime(360): at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:644) 04-07 17:02:53.512: E/AndroidRuntime(360): at android.widget.LinearLayout.onMeasure(LinearLayout.java:280) 04-07 17:02:53.512: E/AndroidRuntime(360): at android.view.View.measure(View.java:7703)
分析:這個錯誤意思是說,在LinearLayout中,measure函數裏,當我要對其child進行基線對齊到第一個child的時候,發現個人內部沒有child,結果基線對齊到第一個child(index爲0)的時候,因爲沒法取到第一個child的信息,因此數組越界了。我當時在網上調研了下,發現有人說這個android1.x的一個bug,mBaselineAlignedChildIndex爲LinearLayout的一個私有成員變量,在android1.x默認值是0,1.x以上默認值是-1,在google code上甚至有人舉報了這個bug。這也就解釋了爲何程序跑在1.6上直接crash,而在2.x上卻正常。java
咱們看一下2.x上這個getBaseline函數android
@Override public int getBaseline() { if (mBaselineAlignedChildIndex < 0) { return super.getBaseline(); } if (getChildCount() <= mBaselineAlignedChildIndex) { throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout " + "set to an index that is out of bounds."); }上述邏輯代表,若是mBaselineAlignedChildIndex = -1的話,直接return了,不然若是child數 <= mBaselineAlignedChildIndex,則訪問越界,也就是文章開始提到的異常。從這段代碼能夠看出,當mBaselineAlignedChildIndex = -1的狀況,在android2.x是不可能出現上述異常的,當時android1.6上mBaselineAlignedChildIndex居然等於0,結果直接拋出異常了。
解決方法:api
1. 就我目前在網上的調研,有人建議出現此類問題的時候,先addView再removeView,這樣或許能解決問題,可是我以爲這樣作很怪,讓人以爲不可理解,無緣無故的add一個又remove掉了,若是不寫註釋的話,別人可能會以爲代碼寫的有誤。數組
2. 放棄了第一種方法,只好另外想辦法,後來在android源碼中找到了思路。請看LinearLayout中的measureHorizontal函數,其中有一段以下ide
if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
// Optimization: don't bother measuring children who are going to use
// leftover space. These views will get measured again down below if
// there is any leftover space.
if (isExactly) {
mTotalLength += lp.leftMargin + lp.rightMargin;
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength +
lp.leftMargin + lp.rightMargin);
}
// Baseline alignment requires to measure widgets to obtain the
// baseline offset (in particular for TextViews). The following
// defeats the optimization mentioned above. Allow the child to
// use as much space as it wants because we can shrink things
// later (and re-measure).
if (baselineAligned) { final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); child.measure(freeSpec, freeSpec); }
}
對比出錯的log,發現調用棧很一致,OnMeasure調用了measureHorizontal,measureHorizontal調用了child的measure。注意到上述代碼的最後幾句,if(baselineAligned)這一句很關鍵,這個變量就表示是否要進行基線對齊,其實這個基線對齊對佈局的影響不大,通常來講咱們不須要設置這個變量,我想既然是基線對齊的時候程序掛的,那爲什麼不取消基線對齊呢,恰好LinearLayout提供了setBaselineAligned這個函數,試着調用setBaselineAligned(false),發現問題解決了。函數
整理一下,判斷一下,若是發現系統版本是1.x(基本只有1.6和1.5),則調用setBaselineAligned(false),其餘api版本什麼也不作。佈局
int mVersionCode = 8;//default value = android 2.2 try { mVersionCode = Integer.valueOf(android.os.Build.VERSION.SDK); Log.d(TAG, "sdk version=" + mVersionCode); } catch (Exception e) { e.printStackTrace(); } //針對android1.6及如下的特殊處理 此爲android的低版本bug if(mVersionCode <= 5) { linearLayoutScrolLayout.setBaselineAligned(false); }