【Android】記今天踩到的一個小坑----Java內存模型

今天在寫一個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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章