forget to call notifyDatasetChanged 錯誤
這幾天一直在完善之前自己的寫的Android應用。想用ViewPager讓之前界面能夠滑動閱讀。但是滑動過程中,經常會莫名其妙地遇到一個問題。莫名其妙地提示ViewPager expected 10 counts,but found 20. forget to call notifyDatasetChanged. 意思很明顯,就是ViewPager的數據集改變了,但是我卻忘了去通過notifyDatasetChanged去通知PagerAdapter數據已經更新了。但是我其實在那個頁面壓根沒有去更新數據。不過根據提示顯然是因爲哪個地方改變了數據的數量。還好我動態更新數據的地方不多。檢查一下代碼,根據自己的思路,立刻發現了問題。我會在已經載入當前列表的時候,去服務器取下一個列表。這樣在我取完下一個列表的時候,數據就變了。然後就出現那個問題了。
雖然沒怎麼浪費時間就解決了問題,但是在面對多個線程數據共享的時候確實得增加一些額外的考慮。PagerAdapter的數據如果改變,是需要調用notifyDatasetChanged方法的。多線程的共享需要考慮同步的問題,這個同步不僅僅是一個synchronize就能夠避免的,每個線程都需要去預防共享的數據被另外一個線程更改了。
ViewPager與Fragment
我在使用ViewPager的時候,使用了FragmentStatePagerAdapter作爲它的Adapter,因爲會有很多Fragment,如何從ViewPager控制它的Fragment就成了一個問題。
當然,直接保存ViewPager的所有Fragment似乎就能夠控制了,但是這實在是不科學,保存起來太耗空間。而實際上ViewPager每次只有幾個View保存着。當前顯示的View以及前後幾個View,在這之外的View都會被destory掉。這樣能夠很好地控制佔用的內存。
但是幸運地是FragmentActivity的FragmentManager提供了一個getFragments的方法,該方法能夠獲取當前所有的Fragment。這樣問題就解決了。獲取Activity中所有的Fragment,然後判斷是不是FragmentStatePagerAdapter的Fragment。這樣ViewPager就能夠直接控制它的Fragment Item了。
對於FragmentPagerAdapter與FragmentStatePagerAdapter的區別,FragmentPagerAdapter會對每一個Fragment進行緩存,而FragmentStatePagerAdapter不會。兩個的使用方法是一樣的。
notifyDatasetChanged做了什麼工作?
每個Adapter都會有private DataSetObservable mObservable = new DataSetObservable();
一個DataSetObservable觀察者。這個觀察者可以註冊registerDataSetObserver
。而notifyDatasetChanged函數做的就是通知註冊這,我的數據已經改變了。
這樣就給了ViewPager監聽Adapter數據集變化的機會。在ViewPager的setAdapter中,有下面這段代碼:
431 if (mAdapter != null) {
432 if (mObserver == null) {
433 mObserver = new PagerObserver();
434 }
435 mAdapter.registerDataSetObserver(mObserver);
436 mPopulatePending = false;
437 final boolean wasFirstLayout = mFirstLayout;
438 mFirstLayout = true;
439 mExpectedAdapterCount = mAdapter.getCount();
440 if (mRestoredCurItem >= 0) {
441 mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
442 setCurrentItemInternal(mRestoredCurItem, false, true);
443 mRestoredCurItem = -1;
444 mRestoredAdapterState = null;
445 mRestoredClassLoader = null;
446 } else if (!wasFirstLayout) {
447 populate();
448 } else {
449 requestLayout();
450 }
451 }
在上面的代碼中,註冊了數據集變化的觀察者。而notifyDatasetChanged就是通知觀察者。
探究