ListView中的觀察者模式

xuyinhuan發表於2016-11-10

雖然現在RecyclerView很好用,也在逐漸替代ListView。很多github的開源大神也在對其進行更加實用的封裝。我現在寫的一個音樂播放器也在使用RecyclerView。但是這些都不阻礙我們學習ListView優秀的原始碼設計。
播放器我想要寫的精美,但現在越寫越多bug,這也應該是我離開大學校園,實習前的最後一個小作品了,接下來的半年多的時間要衝刺複習咯。

進入正題,我用的是Api-23的原始碼。接下來就從原始碼的角度帶你學習ListView中的觀察者模式

當我們開啟非同步執行緒,向服務端拉取資料後,資料來源已經更新了,此時想要更新ListView的檢視以顯示新的資料。
ListView使用了Adapter模式,很簡單隻需一行程式碼就能完成ListView的更新。

那麼這裡引出一個問題,
更新ListView的工作,是Adapter完成的還是ListView自身內部完成的?可以先猜想一下再往下看。
因為我之前已經學習過自定義控制元件,所以我看原始碼之前猜想是ListView完成的。慣性使然,我想到他可能是呼叫了onLayout(),onDraw()等方法呀,去重新佈局,繪製

那接下來就解開疑惑吧。

先找到源頭,從ListView繫結Adapter那裡開始。

ListView和Adapter就是用這行程式碼建立起關聯的。

那麼跟蹤setAdapter方法進去:

方法是這樣開始的

先判斷mAdapter != null && mDataSetObserver != null
mAdapter肯定是不為null的,那麼mDataSetObserver呢?這個引用是哪裡被賦值的,先不管,繼續往下看setAdapter方法。
這裡先分享我看原始碼的方法吧:
剛開始的時候我是很喜歡往深處闖,導致看了一天都無法自拔,思路又散了。現在我看原始碼都是挑重點看,比如這個setAdapter方法,一路看下來都沒有return 語句跳出,那麼就一定會來到if(mAdapter !=null )這個判斷,如下:

到了這裡,我們也就找到了mDataSetObserver,原來是在這裡被賦值的。
現在得出小結論:
1.在ListView的setAdapter方法中,生成了一個AdapterDataSetObserver物件並賦值給mDataSetObserver
2.呼叫Adapter的registerDataSetObserver方法將mDataSetObserver註冊進去。

現在我們好奇的是Adapter的registerDataSetObserver方法。繼續前進。
在BaseAdapter類中找到了registerDataSetObserver方法,並且也找到了經常呼叫的,很熟悉的notifyDataSetChanged方法。如下:

可以看到,在registerDataSetObserver方法中,又呼叫了DataSetObservable的registerObserver方法將傳進來的AdapterDataSetObserver物件註冊進去,那麼這個DataSetObservable又是什麼呢?繼續跟進

這個DataSetObservable原始碼比較少,那就全部貼出

好像看不太懂。mObservers是什麼?竟然沒有registerObserver方法。哈哈,那肯定是父類繼承下來的啊。在DataSetObservable類中暫時沒我們想要知道的資訊,那麼就看看他的父類Observable吧。Observable還是個泛型。不管,看內部實現原理就好

找到了registerObserver方法。程式碼邏輯還挺簡單的。
我們又可以得出小結論:

DataSetObservable的內部維護著一個觀察者集合,即原始碼中的mObservers。當我們的ListView繫結了Adapter,呼叫BaseAdapter的registerDataSetObserver方法時,實際上是在這個觀察者集合mObservers裡將該觀察者新增進來。對ListView來說,這個觀察者就是AdapterDataSetObserver
完成註冊。以上就是setAdapter方法的原始碼分析

再看到BaseAdapter的notifyDataSetChanged()方法

內部呼叫了DataSetObservable的notifyChanged方法

再回到DataSetObservable的原始碼,看到notifyChanged()方法

從觀察者集合裡遍歷出觀察者,並呼叫該觀察者的onChange()方法

很清楚了吧。
當我們呼叫Adapter的notifyDataSetChanged方法更新ListView。
在notifyDataSetChanged方法中又會呼叫DataSetObservable的notifyChanged方法。
而從DataSetObservable的原始碼中,我們知道了在notifyChanged方法中又會遍歷出
AdapterDataSetObserver(觀察者),並呼叫這個觀察者的onChanged()方法。
完畢,底層實現就是這樣。
接下來只需要知道AdapterDataSetObserver(觀察者)的onChanged()方法裡做了什麼就好了。
而AdapterDataSetObserver,是ListView的父類AdapterView的一個內部類。他是真的有onChanged方法的。不信你看

終於揭開謎底,在AdapterDataSetObserver的onChanged()方法裡,實際上是呼叫了View的requestLayout()方法進行重新策略,佈局,繪製整個ListView的子項item view
requestLayout()的原始碼如下:

AdapterView是繼承ViewGroup的,但是ViewGroup並沒有重寫requestLayout()方法。有能力的同學可以繼續深入研究AdapterView到底是怎麼重新佈局的

至此,我們已經解開了開篇的疑惑
綜上所述,AdapterDataSetObserver這個是觀察者,在AdapterDataSetObserver的onChanged函式中,實際上呼叫的是View中的方法完成了整個更新ListView的工作,AdapterDataSetObserver只是在外層進行了包裝,真正的核心功能是ListView,更加準確的說話是ListView的父類AdapterView。

ListView就是通過Adapter模式,觀察者模式,子項複用機制實現了檢視良好的擴充套件性,節約了記憶體開銷,提高了執行效率

相關文章