AndroidAdapterViewView的複用機制分析(轉載)

來源:互聯網
上載者:User

標籤:

AndroidAdapterViewView的複用機制分析

對於ListView、GridView相信大家都不陌生,重寫個BaseView,實現對於的幾個方法,然後就完成了我們的介面展示,並且在大部分情況下,我們載入特別多的Item也不會發生OOM,大家也都明白內部有緩衝機制,都遇到過ItemView複用帶來的一些問題,比如非同步載入圖片,最終造成介面顯示的混亂,我們一般會使用setTag,然後回調顯示時,避免造成混亂。

設想1:拿ListView為例,如果ListView的ItemView複用機制,所有的ItemView複用同一個,如果在多線程下載圖片的情況下,可能最終只有最後一個View顯示圖片吧,因為你前面的設定setTag(url),後面馬上就會將你的Tag的值覆蓋掉,最終findViewByTag找到的都是最後一個。由此可見ListView緩衝的不是一個,至少是一螢幕可顯示的數量。也就是說ListView維護著一個ItemView的池子。

跟大家解釋下,為啥緩衝了一個螢幕的可顯示最大的ItemView數量的池子,我們可能上千個ItemView,僅依靠Tag就能實現不混亂呢。

情景:螢幕每次顯示7個Item,ListView一共1000個Item,每個Item上顯示一張從網路下載的圖片。

getView的代碼大概是這樣的:

@Overridepublic View getView(int position, View convertView, ViewGroup parent){final String url = getItem(position);View view;if (convertView == null){view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);} else{view = convertView;}final ImageView photo = (ImageView) view.findViewById(R.id.photo);// 給ImageView設定一個Tag,保證非同步載入圖片時不會亂序photo.setTag(url);new LoadImgTask(photo).execute(url);return view;}
下載完成圖片,進行photo.getTag().equals(url)來防止圖片顯示的混亂。

如果我們開啟介面,開啟了7個線程去下載,此時緩衝了這7個ItemView,現在滑動螢幕顯示另外下一屏,此時7個ItemView都會複用,會把第一屏設定的Tag全部覆蓋掉,沒錯就是覆蓋掉了,又開啟7個線程去下載圖片,當第一屏的ItemView的圖片下載完成後,如果直接findViewByTag然後設定圖片會顯示在第二屏上,就混亂了,所以一般在顯示前都會判斷photo.getTag().equals(url);確定了再顯示,也就是說第一屏的ItemView圖片下載完了,但是Tag被覆蓋了,所以即使下載完成了,也不會有任何顯示。這就解釋了為什麼我們防止混亂的代碼需要那樣去寫。

好了,下面從源碼角度看一眼ListView內部到底是如何進行緩衝的:

跟著ListView,進入父類AbsList,會發現這樣一個變數:

 /**     * The data set used to store unused views that should be reused during the next layout     * to avoid creating new ones     */    final RecycleBin mRecycler = new RecycleBin();

注釋的意思上用一個資料集來儲存應當在下一個布局重用的View,避免重新建立新的布局。這個對象應該就是對我們緩衝管理的核心類了。繼續看這個類,這是一個內部類:

/**     * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of     * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the     * start of a layout. By construction, they are displaying current information. At the end of     * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that     * could potentially be used by the adapter to avoid allocating views unnecessarily.     *     * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)     * @see android.widget.AbsListView.RecyclerListener     */    class RecycleBin {    private View[] mActiveViews = new View[0];                                                                                                                private ArrayList<View>[] mScrapViews;                                                                                                                 ....                                                                                                                                                 }   
大概意思:這個類是用來協助在滑動布局時重用View的,RecycleBin包含了兩個層級的儲存,ActiveViews和ScrapViews,ActiveViews儲存的是第一次顯示在螢幕上的View;所有的ActiveViews最終都會被移到ScrapViews,ScrapViews儲存的是有可能被adapter複用的View。
現在很明確了AbsListView緩衝依賴於兩個數組,一個數組儲存螢幕上當前現實的ItemView,一個顯示從螢幕下移除的且可能會被複用的ItemView。下面看ListView裡面的代碼:

@Override    protected void layoutChildren() {if (dataChanged) {for (int i = 0; i < childCount; i++) {recycleBin.addScrapView(getChildAt(i));  }} else {recycleBin.fillActiveViews(childCount, firstPosition);}....}

 /*** Fill ActiveViews with all of the children of the AbsListView.** @param childCount The minimum number of views mActiveViews should hold* @param firstActivePosition The position of the first view that will be stored in*        mActiveViews*/void fillActiveViews(int childCount, int firstActivePosition) {if (mActiveViews.length < childCount) {mActiveViews = new View[childCount];}mFirstActivePosition = firstActivePosition;final View[] activeViews = mActiveViews;for (int i = 0; i < childCount; i++) {View child = getChildAt(i);activeViews[i] = child; }}

可以看出,如果資料發生變化則把當前的ItemView放入ScrapViews中,否則把當前顯示的ItemView放入ActiveViews中。那麼咱們關鍵的getView方法到底是在哪調用呢,下面看RecycleBin中的方法:

/**     * Get a view and have it show the data associated with the specified     * position. This is called when we have already discovered that the view is     * not available for reuse in the recycle bin. The only choices left are     * converting an old view or making a new one.     *     * @param position The position to display     * @param isScrap Array of at least 1 boolean, the first entry will become true if     *                the returned view was taken from the scrap heap, false if otherwise.     *      * @return A view displaying the data associated with the specified position     */    View obtainView(int position, boolean[] isScrap) {isScrap[0] = false;        View scrapView;        scrapView = mRecycler.getScrapView(position);        View child;        if (scrapView != null) {                      child = mAdapter.getView(position, scrapView, this);            if (child != scrapView) {                mRecycler.addScrapView(scrapView);                           } else {                isScrap[0] = true;                child.dispatchFinishTemporaryDetach();            }        } else {            child = mAdapter.getView(position, null, this);                 }        return child;    }

可以看到,這個方法就是返回當前一個布局使用者當前Item的顯示,首先根據position去ScrapView中找,找到後調用我們的getView,此時getView裡面的convertView!=null了,然後getView如果返回的View發生變化,緩衝下來,否則convertView==null了。

好了,主要是為了讓大家瞭解,AbsListView為什麼我們可以通過一個Tag的設定保證其正確的顯示,以及緩衝機制在AbsListView到底是怎麼實現的,鑒於原始碼實在太長,只能大概的根據代碼瞭解一下原理。


最後,各位看官,沒事留個言,頂一個唄~





原文連結本文由豆約翰部落格備份專家遠程一鍵發布

AndroidAdapterViewView的複用機制分析(轉載)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.