1. 程式人生 > >糾正:Android Attempt to write to field 'int android.support.v7.widget.RecyclerView$ViewHolder...

糾正: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,就算什麼頭緒都沒有,挨個打斷點最終也能找出問題