1. 程式人生 > >java.lang.IllegalArgumentException: No view found for id 0xad7b9d70 (unknown) for fragment, 爬坑

java.lang.IllegalArgumentException: No view found for id 0xad7b9d70 (unknown) for fragment, 爬坑

java.lang.IllegalArgumentException: No view found for id 0xad7b9d70 (unknown) for fragment, 爬出一個findViewById的坑

最近遇到一個比較奇怪且難定位的異常,是由新功能開發而導致的,在4.4模擬器上面初始化某個介面的時候會出現崩潰,崩潰資訊如下。這個異常在真機以及5.0以上的模擬器上都不會出現。

java.lang.IllegalArgumentException: No view found for id 0xad7b9d70 (unknown) for fragment MyFragment{ad792440 #0 id=0xad7b9d70 android:switcher:-1384407696:0}
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1102) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801) at android.support.v4.app.FragmentManagerImpl
.execSingleAction(FragmentManager.java:1638) at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:679) at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143) at android.support.v4.view.ViewPager.populate(ViewPager.java
:1240) at android.support.v4.view.ViewPager.populate(ViewPager.java:1088) at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1614) at android.view.View.measure(View.java:16497) at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:719) at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:455) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1404) at android.widget.LinearLayout.measureVertical(LinearLayout.java:695) at android.widget.LinearLayout.onMeasure(LinearLayout.java:588) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2291) at android.view.View.measure(View.java:16497)

新功能的介面邏輯還算簡單,一個viewPager + 兩個fragment組成,異常報出來的資訊是其中一個fragment找不到id為0xad7b9d70的view。堆疊資訊全部都是原始碼內的,沒有找到容易定位的關鍵資訊。於是很自然的順著異常報出來的資訊進入MyFragment裡面去看邏輯(掉進坑裡),毫無疑問,找不到明顯的問題。於是把MyFragment從viewPagerAdapter內移除,只加載一個Fragment,發現異常依舊,並且異常資訊顯示為另一個OtherFragment了。再把OtherFragment也移除掉,介面正常載入了,看來應該是Fragment載入進viewPager的過程種出現了問題。

在查看了自定義的CustomViewPager程式碼找不到問題後,跟著堆疊資訊進入FragmentManager.moveToState()原始碼檢視,發現了下面一段程式碼:

container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
    String resName;
    try {
        resName = f.getResources().getResourceName(f.mContainerId);
    } catch (NotFoundException e) {
        resName = "unknown";
    }
    throwException(new IllegalArgumentException(
            "No view found for id 0x"
            + Integer.toHexString(f.mContainerId) + " ("
            + resName
            + ") for fragment " + f));
}

異常確實是從這裡丟擲來的,原始是尋找container的時候找到了null,這裡的f是引數傳進來的Fragment,異常資訊中報出的fragment就是當前引數f。通過除錯,發現在真機和5.0以上的模擬器,mContainer.onFindViewById(f.mContainerId)都是正常獲取到一個ViewGroup,唯獨在4.4模擬器上面這句程式碼返回了null,看來問題就出來這裡。繼續除錯進入原始碼findViewById(),程式碼如下:

@Nullable
public final View findViewById(@IdRes int id) {
    if (id < 0) {
        return null;
    }
    return findViewTraversal(id);
}

當除錯到這個位置時,id<0這個條件成立了,直接返回了null,看來問題是出現在id這個位置。當Fragment載入到ViewPager上面時由於尋找容器的id小於0直接返回null,最終導致丟擲異常崩潰。

檢視跟id相關的的程式碼邏輯:

mViewPager = new CustomViewPager(getContext(), null);
mViewPager.setId(mViewPager.hashCode());
mViewPager.setAdapter(mPagerAdapter);

問題的關鍵點,應該是找到了,viewPager的id是通過setId方法設定進去的,並且沒有遵循這個api的使用規範,設定 R.id.xxx 這種方式來設定id。
Object.hashCode()方法在不同的版本中會有不同的表現,目前僅在4.4的模擬器上發現它會返回負數。

修復方案:
1. mViewPager.setId(R.id.pager); (推薦)
2. mViewPager.setId(Math.abs(mViewPager.hashCode()));