今天在寫一個RecyclerView的Demo,大致的狀況就是請求網絡分頁加載數據,解析成bean然後填到列表裏,展示瀑布流出來.但是寫完之後列表卻一直都是空的,但是斷點裏也能清楚地看到數據解析成功,被設置到了adapter中,反覆看了好久,最後還是在同事的指點下,跳出了坑,其實問題是Java引用的問題,我先把錯誤的代碼貼出來.大家觀摩一下.
Adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<PostViewHolder> {
private Context context;
public List<PostsEntity> list = new ArrayList<>();
public RecyclerViewAdapter(Context context, List<PostsEntity> list) {
this.context = context;
this.list = list;
}
@Override
public PostViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
PostViewHolder holder = new PostViewHolder(LayoutInflater.from(
context).inflate(R.layout.post_detail_item, viewGroup, false));
return holder;
}
@Override
public void onBindViewHolder(PostViewHolder viewHolder, int position) {
//填充界面等操作...
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
}
Activity:
public class HomeActivity extends AppCompatActivity {
@Bind(R.id.recyclerview)
RecyclerView recyclerview;
private List<PostsEntity> originalList;
private RecyclerViewAdapter adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ButterKnife.bind(this);
init();
loadData();
}
private void init() {
list = new ArrayList<>();
adapter = new RecyclerViewAdapter(HomeActivity.this, originalList);
LinearLayoutManager mManager = new LinearLayoutManager(HomeActivity.this, LinearLayoutManager.VERTICAL, false);
recyclerview.setAdapter(adapter);
recyclerview.setLayoutManager(mManager);
}
private void loadData() {
OkHttpUtils.get().url(URL).build().execute(new StringCallback() {
@Override
public void onError(Call call, Exception e) {
}
@Override
public void onResponse(final String response) {
Gson gson = new Gson();
final PostBean bean = gson.fromJson(response, PostBean.class);
assembleData(bean);
}
});
}
private void assembleData(PostBean bean) {
originalList = bean.getPosts();<span style="white-space:pre"> </span>//問題就出現在了這裏!!!
adapter.notifyDataSetChanged();
}
}
好了直接看出問題的可以打我了,沒看出來的往下看,聽我縮.
講解原因之前,先介紹一下Java的內存模型,其實在自己剛開始學習Java的時候(一年多之前)記過這樣一條筆記:
但是,如果當初能夠真正的理解這些概念,也不至於有今天的事情,還是那句話:紙上得來終覺淺,絕知此事要躬行.
上面的話大致可以這樣理解,Book book = new Book();這句話其實創建了兩個變量:book爲引用變量,存儲在堆棧中;new出來的Book()爲值變量,存在堆中.book的值就是Book()的地址.這是分析上面問題的先決條件.
下面再上一張我自己畫的關於這段出錯程序的圖,
上圖一共分爲6部,三個區域(從左至右爲Activity的棧空間, Java的堆空間,Adapter的棧空間),我們一步一步看.
1.在Activity的onCreate()方法中我們初始化一個空的originalList,用來存放接下來從服務端獲取的bean.
2.我們在new RecyclerViewAdapter的時候,把originalList以構造方法參數的形式傳進去.
3.在Adapter的構造方法內部,this.list = list;這一行就是第三步.
4.在loadData()方法中我們請求到了真正的數據bean.getPosts(),這是一個以PostEntity爲元素的列表.
5和6.在我們出錯的那一行,originalList = bean.getPosts();此時original作爲一個引用變量,它的值變了,不再指向上面的那塊內存區域,從而指向下面的那塊內存區域.
可是這時候我們的adapter內的list還是指向原來的內存區域,那塊區域裏還是空的,自然RecyclerView就是空的了,不展示任何東西.所以問題癥結所在也就一目瞭然了.
下面貼一下正確的做法:
private void assembleData(PostBean bean) {
list.clear();
list.addAll(bean.getPosts())
adapter.notifyDataSetChanged();
}
最後要做一下總結,出現這個問題還是因爲自己的基礎不過關,當初是記過筆記,但大多還是流於表面形式,沒有在編碼實踐中有所體會.正所謂"基礎不牢,地動山搖",現在踩得每一個坑都是自己以前埋下的.
最後感謝一下今天幫我出坑的我司小夥伴(技術沒的說,虐我無壓力):@寫代碼的猴子.(知乎網紅,我會卵縮麼?)
博客:寫代碼的猴子
github:laobie
知乎:Jaeger
相關問題鏈接請戳:http://stackoverflow.com/questions/21598621/notifydatasetchanged-not-working