雖然現在RecyclerView很好用,也在逐漸替代ListView。很多github的開源大神也在對其進行更加實用的封裝。我現在寫的一個音樂播放器也在使用RecyclerView。但是這些都不阻礙我們學習ListView優秀的原始碼設計。
播放器我想要寫的精美,但現在越寫越多bug,這也應該是我離開大學校園,實習前的最後一個小作品了,接下來的半年多的時間要衝刺複習咯。
進入正題,我用的是Api-23的原始碼。接下來就從原始碼的角度帶你學習ListView中的觀察者模式
當我們開啟非同步執行緒,向服務端拉取資料後,資料來源已經更新了,此時想要更新ListView的檢視以顯示新的資料。
ListView使用了Adapter模式,很簡單隻需一行程式碼就能完成ListView的更新。
1 |
mAdapter.notifyDataSetChanged(); |
那麼這裡引出一個問題,
更新ListView的工作,是Adapter完成的還是ListView自身內部完成的?可以先猜想一下再往下看。
因為我之前已經學習過自定義控制元件,所以我看原始碼之前猜想是ListView完成的。慣性使然,我想到他可能是呼叫了onLayout(),onDraw()等方法呀,去重新佈局,繪製
那接下來就解開疑惑吧。
先找到源頭,從ListView繫結Adapter那裡開始。
1 |
mListView.setAdapter(mAdapter); |
ListView和Adapter就是用這行程式碼建立起關聯的。
那麼跟蹤setAdapter方法進去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public void setAdapter(ListAdapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } mOldSelectedPosition = INVALID_POSITION; mOldSelectedRowId = INVALID_ROW_ID; // AbsListView#setAdapter will update choice mode states. super.setAdapter(adapter); if (mAdapter != null) { mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); mOldItemCount = mItemCount; mItemCount = mAdapter.getCount(); checkFocus(); mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); int position; if (mStackFromBottom) { position = lookForSelectablePosition(mItemCount - 1, false); } else { position = lookForSelectablePosition(0, true); } setSelectedPositionInt(position); setNextSelectedPositionInt(position); if (mItemCount == 0) { // Nothing selected checkSelectionChanged(); } } else { mAreAllItemsSelectable = true; checkFocus(); // Nothing selected checkSelectionChanged(); } requestLayout(); } |
方法是這樣開始的
1 2 3 |
if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } |
先判斷mAdapter != null && mDataSetObserver != null
mAdapter肯定是不為null的,那麼mDataSetObserver呢?這個引用是哪裡被賦值的,先不管,繼續往下看setAdapter方法。
這裡先分享我看原始碼的方法吧:
剛開始的時候我是很喜歡往深處闖,導致看了一天都無法自拔,思路又散了。現在我看原始碼都是挑重點看,比如這個setAdapter方法,一路看下來都沒有return 語句跳出,那麼就一定會來到if(mAdapter !=null )這個判斷,如下:
1 2 3 4 5 6 7 8 9 10 11 |
if (mAdapter != null) { mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); mOldItemCount = mItemCount; mItemCount = mAdapter.getCount(); checkFocus(); mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); //程式碼省略 } |
到了這裡,我們也就找到了mDataSetObserver,原來是在這裡被賦值的。
現在得出小結論:
1.在ListView的setAdapter方法中,生成了一個AdapterDataSetObserver物件並賦值給mDataSetObserver
2.呼叫Adapter的registerDataSetObserver方法將mDataSetObserver註冊進去。
現在我們好奇的是Adapter的registerDataSetObserver方法。繼續前進。
在BaseAdapter類中找到了registerDataSetObserver方法,並且也找到了經常呼叫的,很熟悉的notifyDataSetChanged方法。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { private final DataSetObservable mDataSetObservable = new DataSetObservable(); public boolean hasStableIds() { return false; } public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } /** * Notifies the attached observers that the underlying data has been changed * and any View reflecting the data set should refresh itself. */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } /** * Notifies the attached observers that the underlying data is no longer valid * or available. Once invoked this adapter is no longer valid and should * not report further data set changes. */ public void notifyDataSetInvalidated() { mDataSetObservable.notifyInvalidated(); } //程式碼省略 } |
可以看到,在registerDataSetObserver方法中,又呼叫了DataSetObservable的registerObserver方法將傳進來的AdapterDataSetObserver物件註冊進去,那麼這個DataSetObservable又是什麼呢?繼續跟進
這個DataSetObservable原始碼比較少,那就全部貼出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class DataSetObservable extends Observable<DataSetObserver> { /** * Invokes {@link DataSetObserver#onChanged} on each observer. * Called when the contents of the data set have changed. The recipient * will obtain the new contents the next time it queries the data set. */ public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } /** * Invokes {@link DataSetObserver#onInvalidated} on each observer. * Called when the data set is no longer valid and cannot be queried again, * such as when the data set has been closed. */ public void notifyInvalidated() { synchronized (mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onInvalidated(); } } } } |
好像看不太懂。mObservers是什麼?竟然沒有registerObserver方法。哈哈,那肯定是父類繼承下來的啊。在DataSetObservable類中暫時沒我們想要知道的資訊,那麼就看看他的父類Observable吧。Observable還是個泛型。不管,看內部實現原理就好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public abstract class Observable<T> { /** * The list of observers. An observer can be in the list at most * once and will never be null. */ protected final ArrayList<T> mObservers = new ArrayList<T>(); /** * Adds an observer to the list. The observer cannot be null and it must not already * be registered. * @param observer the observer to register * @throws IllegalArgumentException the observer is null * @throws IllegalStateException the observer is already registered */ public void registerObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { if (mObservers.contains(observer)) { throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); } } //程式碼省略 } |
找到了registerObserver方法。程式碼邏輯還挺簡單的。
我們又可以得出小結論:
DataSetObservable的內部維護著一個觀察者集合,即原始碼中的mObservers。當我們的ListView繫結了Adapter,呼叫BaseAdapter的registerDataSetObserver方法時,實際上是在這個觀察者集合mObservers裡將該觀察者新增進來。對ListView來說,這個觀察者就是AdapterDataSetObserver
完成註冊。以上就是setAdapter方法的原始碼分析
再看到BaseAdapter的notifyDataSetChanged()方法
1 2 3 |
public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } |
內部呼叫了DataSetObservable的notifyChanged方法
再回到DataSetObservable的原始碼,看到notifyChanged()方法
1 2 3 4 5 6 7 8 9 10 11 |
public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } |
從觀察者集合裡遍歷出觀察者,並呼叫該觀察者的onChange()方法
很清楚了吧。
當我們呼叫Adapter的notifyDataSetChanged方法更新ListView。
在notifyDataSetChanged方法中又會呼叫DataSetObservable的notifyChanged方法。
而從DataSetObservable的原始碼中,我們知道了在notifyChanged方法中又會遍歷出
AdapterDataSetObserver(觀察者),並呼叫這個觀察者的onChanged()方法。
完畢,底層實現就是這樣。
接下來只需要知道AdapterDataSetObserver(觀察者)的onChanged()方法裡做了什麼就好了。
而AdapterDataSetObserver,是ListView的父類AdapterView的一個內部類。他是真的有onChanged方法的。不信你看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; @Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; mItemCount = getAdapter().getCount(); // Detect the case where a cursor that was previously invalidated has // been repopulated with new data. if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null && mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState); mInstanceState = null; } else { rememberSyncState(); } checkFocus(); requestLayout(); } //程式碼省略 } |
終於揭開謎底,在AdapterDataSetObserver的onChanged()方法裡,實際上是呼叫了View的requestLayout()方法進行重新策略,佈局,繪製整個ListView的子項item view
requestLayout()的原始碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } } |
AdapterView是繼承ViewGroup的,但是ViewGroup並沒有重寫requestLayout()方法。有能力的同學可以繼續深入研究AdapterView到底是怎麼重新佈局的
至此,我們已經解開了開篇的疑惑
綜上所述,AdapterDataSetObserver這個是觀察者,在AdapterDataSetObserver的onChanged函式中,實際上呼叫的是View中的方法完成了整個更新ListView的工作,AdapterDataSetObserver只是在外層進行了包裝,真正的核心功能是ListView,更加準確的說話是ListView的父類AdapterView。
ListView就是通過Adapter模式,觀察者模式,子項複用機制實現了檢視良好的擴充套件性,節約了記憶體開銷,提高了執行效率