PullToRefreshScrollView 导致的 java.lang.IllegalArgumentException
日志信息
java.lang.RuntimeException:
Unableto start activity ComponentInfo {com.renfenqi.shop/com.renfenqi.shop.ui.main.MainActivity}:
java.lang.IllegalArgumentException:
Wrong state class, expecting View State but received class android.widget.ScrollView$SavedState instead. This usually happens when two views of different type have the same id in the same hierarchy. This view's id is id/scrollview. Make sure other views do not use the same id.
2013年以前,Android-PullToRefresh 还是很火的。虽然如今SwipeRefreshLayout + RecyclerView 已然成为主流,但一些陈年的老项目因为没有重构,导致PullToRefresh任然在项目中的某些地方发挥着余热。我就接手了一个这样的项目,然后我在bugly上发现了一个莫名其妙的Bug,而且无论如何我也不能在本地重现。所以,我考虑到了会不会只是在特定的机型下才会重现,果不其然。
所有的bug都集中在 oppo(5.1.X, 4.4.X)/vivo(5.1.1)
这也就说明,这个bug的起因就是产商定制造成的。那么,我们该如何解决呢。
我们注意这里有这么一句话:
Make sure other views do not use the same id.
可当我检查id的时候,发现在同一个layout.xml并没有重复的id(这是必然的,不然也无法通过编译),那问题在哪呢?
我翻开了PullToRefreshScrollView发现里面有这么一段代码
@Override protected ScrollView createRefreshableView(Context context, AttributeSet attrs) { ScrollView scrollView; if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { scrollView = new InternalScrollViewSDK9(context, attrs); } else { scrollView = new ScrollView(context, attrs); } scrollView.setId(R.id.scrollview); return scrollView; }
哈哈,果然...在其内部的构造方法中,动态生成了一个id为scrollview的ScrollView。这就导致了部分产商的手机不能兼容,从而引起Crash。
OK,找到了病因,解决起来就很方便了。
解决方案一:
修改layout.xml中与scrollview同名的id号,并保证在引用到PullToRefreshScrollView的layout.xml不出现scrollview;
<com.soft.pullToRefresh.libray.PullToRefreshScrollView android:id="@+id/scrollview" android:layout_width="match_parent" android:layout_height="fill_parent" />
修改成
<com.soft.pullToRefresh.libray.PullToRefreshScrollView android:id="@+id/pulltorefreshscrollview" android:layout_width="match_parent" android:layout_height="fill_parent" />
修改方案二:
@Override protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); }
修改成
@Overrideprotected void onRestoreInstanceState(Parcelable state) { try { super.onRestoreInstanceState(state); } catch (Exception e) { } state = null; }
这里,第二种方法虽然可以保证程序不发生Crash,但程序内部依然抛出了java.lang.RuntimeException。所以这种做法我是不建议的。
当然,系统为什么会抛出这个异常,这个异常最终指向哪里,有兴趣的小伙伴可以研究一下下面这几篇文章:
因为我一开始并没有发现PullToRefreshScrollView中动态设置的id,所以还花了大量的时间去爬这个坑。
https://blog.****.net/iamcxl369/article/details/78428558
View的onSaveInstanceState和onRestoreInstanceState过程分析