糾正:Android Attempt to write to field 'int android.support.v7.widget.RecyclerView$ViewHolder...
最近發現recyclerview的一個異常,其他部落格寫的錯的亂七八糟,在此糾正一下這個異常:
Attempt to write to field 'int android.support.v7.widget.RecyclerView$ViewHolder.mItemViewType' on a null object reference
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:5555)
....
首先教大家如何看這種沒有報錯行的崩潰日誌:
1.開啟崩潰的最後一行RecyclerView$Adapter.createViewHolder(由於電腦api版本和手機api版本可能不同,報錯行號不一定是對的,自己參考報錯資訊ViewHolder.mItemViewType找到對應的行號)
public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;//可以看出是這行崩潰導致的
TraceCompat.endSection();
return holder;
}
2.空指標異常知道吧:接著就看holder怎麼是null的
3.onCreateViewHolder我就不用多解釋了吧
4.檢查自己的onCreateViewHolder肯定有返回null的情況
有的人可能很費解:返回null是因為自己知道這是一個不可能出現的型別,rv是不是有bug?
在這個崩潰日誌上我可以肯定的告訴你,肯定是你的程式碼問題
如下我列出最常見的錯誤程式碼:
1.先看看adapter類,有人會問:加了高大上的super做容錯(super.getItemViewType是0寫不寫沒啥用)這樣寫有錯嗎?
@Override protected BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater) { switch (viewType) { case TYPE_HEADER: return new XXViewHolder(inflater.inflate(R.layout.vw_header, parent, false)); case TYPE_FOTTER: return new YYViewHolder(inflater.inflate(R.layout.frag_skintest1, parent, false)); default: return null; } } @Override public int getItemViewType(int position) { if (position == 0) { return TYPE_HEADER; } else if (position<=mList.size()){ return TYPE_FOTTER; } return super.getItemViewType(position); }
2.這樣寫肯定也沒錯,但是下面的程式碼就是問題了
public void httpData(){
mList.clear();
HttpUtils.postDialog(this,"",//網路請求框架
MapUtils.getInstance(),
DataBean.class,
new OKHttpListener<DataBean>() {
@Override
public void onSuccess(DataBean bean) {//成功回撥
mList.addAll(bean.data);
mAdapter.notifyDataSetChanged();
}
});
}
很多人都喜歡在請求之前去clear自己的list,然後在網路請求完成之後再addAll,但是:
你在請求網路這段時間,rv是不會每次都呼叫getItemCount的,這時候rv會認為count還是原來的(比如100).
這個時候rv去getItemViewType(99),你已經把你的list物件clear了,得到的結果自然是0了.
然後rv去拿這個0去onCreateViewHolder,null就此產生了
3.正確的寫法是:每次list發生變化的時候都應該重新整理adapter來告訴rv資料發生變化了
public void httpData(){
HttpUtils.postDialog(this,"",
MapUtils.getInstance(),
DataBean.class,
new OKHttpListener<DataBean>() {
@Override
public void onSuccess(DataBean bean) {
mList.clear();
mList.addAll(bean.data);
mAdapter.notifyDataSetChanged();
}
});
}
或者你也可以先clear,然後立即notify,等網路請求完成addAll,然後立即notify
4.還有比如getItemViewType和onCreateViewHolder的type不對應等其他問題(博主的腦洞也是有限的).
首先找到可疑的位置,多debug一下就看出來了
結論:
1.要學會正確的看錯誤日誌
2.資料改變,adapter要及時重新整理
3.不要看到未知錯誤就度娘,要學會debug,就算什麼頭緒都沒有,挨個打斷點最終也能找出問題