Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 通過Android源碼分析再探觀察者模式(二)

通過Android源碼分析再探觀察者模式(二)

編輯:關於Android編程

接著上篇文章,現在在通過Android實際開發和源碼再探觀察者模式,listview是我們日常開發中必用的控件,雖然之前就有listview的替代品(recyclerview),現在不去深究兩個控件到底誰好誰差,但有一點需要強調下,它們之間有一大相同點,都是通過觀察者模式去實現數據更新。

首先,我們先去看一個簡單的例子

/**
 * 
 * created by zero on2016-6-1
 * 
 * 通過listview再探觀察者模式
 * 
 */
public class MainActivity extends Activity
{
    private ListView lv_simple;
    private ArrayAdapter adapter;
    private Button btn_add;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_listview);
        lv_simple = (ListView) findViewById(R.id.lv_simple);
        btn_add = (Button) findViewById(R.id.btn_add);
        btn_add.setOnClickListener(new OnClickListener()
        {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                adapter.add("will");
                adapter.add("go");
                adapter.add("on");
                adapter.notifyDataSetChanged();
            }
        });
        adapter = new ArrayAdapter(this,
                android.R.layout.simple_expandable_list_item_1);
        adapter.add("my");
        adapter.add("heart");
        lv_simple.setAdapter(adapter);
    }
}

結果

布局文件就一個listview加上一個button,沒必要貼代碼了,現在直奔主題,核心分析listview和ArrayAdapter之間的關系,我們都知道listview添加adapter通過setAdapter(adapter)方法,當刷新的時候,需要調用adapter.notifyDataSetChanged()方法,首先,我們先看下ArrayAdapter父類BaseAdapter的源碼<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> 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(); } public boolean areAllItemsEnabled() { return true; } public boolean isEnabled(int position) { return true; } public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } public int getItemViewType(int position) { return 0; } public int getViewTypeCount() { return 1; } public boolean isEmpty() { return getCount() == 0; } }

之前都是繼承BaseAdapter,卻很少了解它的源碼,裡面部分方法,在最初工作的兩年裡面,壓根沒看到過,先對一些方法作出解釋,如下:

int getItemViewType(int position)  
int getViewTypeCount()

如果ListView需要顯示多種類型的內容,就需要有不同的緩存拿來使用。比如一個listview裡面有好幾種類型的item,就像基數是一種item,偶數是一種item,或者更復雜的布局,這時候這兩個方法就可以實現我們需要的頁面。

long getItemId(int position)  
boolean hasStableIds() 

getItemId是干嘛用的?在調用 invalidateView()時,ListView會刷新顯示內容。如果內容的id是有效的,系統會跟據id來確定當前顯示哪條內容,也就是firstVisibleChild的位置。id是否有效通過hasStableIds()確定。

boolean areAllItemsEnabled()  
boolean isEnabled(int position) 

在我們點擊item的時候,系統會有一個默認的顏色,如果不希望Item點擊時出現背景顏色,就使用這兩個方法。它們分別針對所有和單個View實現。這個確實有點惡心,之前一直在listview裡面找方法,最後沒轍了,就在listview裡面設置背景色透明,我相信一定會有好多小伙伴和我一樣這麼做的,哈哈。

registerDataSetObserver(DataSetObserver observer)  
unregisterDataSetObserver(DataSetObserver observer) 

對於這兩個方法,這裡作為重點講述,通過上一篇博客,看到observer是不是瞬間很親切,根據字義我們應該就明白這是一個注冊和取消注冊的操作,之前也講述過,observer和observable通過注冊相關聯,當observable發生改變的時候,observer會和observable保持同步操作,這裡DataSetObservable的是干嘛用的?看到名字就知道是和Observable相關,我們在回憶一下,上文提到observable中一個很重要的條件就是必須要有一個容器,DataSetObservable會不會和容器相關呢?我們繼續追查下去。

/**
 * A specialization of {@link Observable} for {@link DataSetObserver}
 * that provides methods for sending notifications to a list of
 * {@link DataSetObserver} objects.
 */
public class DataSetObservable extends Observable {
    /**
     * 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();
            }
        }
    }
}

先看到這裡,通過上述的注釋可知道,當observable發生改變的時候,便會通知DataSetObserver對象的列表發生對應的改變。現在,好像明白了點什麼,但是我們沒有找到容器,我們繼續追查下去,接下來,我們再看下DataSetObserver的父類,如下:

public abstract class Observable {
    /**
     * The list of observers.  An observer can be in the list at most
     * once and will never be null.
     */
    protected final ArrayList mObservers = new ArrayList();

    /**
     * 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);
        }
    }

    /**
     * Removes a previously registered observer. The observer must not be null and it
     * must already have been registered.
     * @param observer the observer to unregister
     * @throws IllegalArgumentException the observer is null
     * @throws IllegalStateException the observer is not yet registered
     */
    public void unregisterObserver(T observer) {
        if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            int index = mObservers.indexOf(observer);
            if (index == -1) {
                throw new IllegalStateException("Observer " + observer + " was not registered.");
            }
            mObservers.remove(index);
        }
    }

    /**
     * Remove all registered observers.
     */
    public void unregisterAll() {
        synchronized(mObservers) {
            mObservers.clear();
        }
    }
}

我們在理一下,重新提取核心代碼,如下:

public class DataSetObservable extends Observable
public abstract class Observable {    
    protected final ArrayList mObservers = new ArrayList();
    }

現在一目了然了,我們找到了容器了,Observable類裡面就是注冊observer、取消特定某個observer的注冊,還有取消容器中所有observer的注冊,DataSetObservable裡面提供了notifyChanged()方法,遍歷mObservers,通知所有observer發生對應的改變,另外,還提供了notifyInvalidated()方法,在數據源失效的時候會調用這個方法。

現在,我們在分析DataSetObserver,毫無疑問,這個肯定就是和Observer相關喽。先看源碼,如下:

public abstract class DataSetObserver {
    /**
     * This method is called when the entire data set has changed,
     * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
     */
    public void onChanged() {
        // Do nothing
    }

    /**
     * This method is called when the entire data becomes invalid,
     * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
     * {@link Cursor}.
     */
    public void onInvalidated() {
        // Do nothing
    }

對應DataSetObservable中的兩個遍歷,一切都是那麼的清晰,O(∩_∩)O哈哈~

listview和adapter關聯是通過setAdapter(adapter)源碼如下:

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();
    }

listview有4000行左右的代碼,此處就拷貝一些必要代碼,通過以上代碼可知,當mAdapter和mDataSetObserver同時不為null的時候,便會把mDataSetObserver取消注冊,當mAdapter不為null的時候,便會在mAdapter裡面進行注冊了一個觀察者。s當數據集發生改變的時候,我們通過adapter.notifyDataSetChanged()方法去改變數據,點進去後,追查到的代碼如下:

@Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
        mNotifyOnChange = true;
    }

這是ArrayAdapter裡面的代碼,我們接著追查它的父類BaseAdapter

public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

最後還是指向了DataSetObservable的notifyChanged()方法,一個輪回後,又重新回到了原點,遍歷通知發生改變。

現在還有一個最後問題,是怎樣更新界面的,onchanged()方法裡面什麼都沒操作,到底是在哪裡更新的呢?好吧,我們現在再把目光朝前放一放,回到listview源碼中,剛剛提到如果adapter不為null的時候,便會注冊一個observer到adapter中,關鍵的兩行代碼提取,如下:

mDataSetObserver = new AdapterDataSetObserver();         mAdapter.registerDataSetObserver(mDataSetObserver);

當我們一路追蹤AdapterDataSetObserver的時候,發現AdapterDataSetObserver是AdapterView的內部類,代碼如下:

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();
        }

        @Override
        public void onInvalidated() {
            mDataChanged = true;

            if (AdapterView.this.getAdapter().hasStableIds()) {
                // Remember the current state for the case where our hosting activity is being
                // stopped and later restarted
                mInstanceState = AdapterView.this.onSaveInstanceState();
            }

            // Data is invalid so we should reset our state
            mOldItemCount = mItemCount;
            mItemCount = 0;
            mSelectedPosition = INVALID_POSITION;
            mSelectedRowId = INVALID_ROW_ID;
            mNextSelectedPosition = INVALID_POSITION;
            mNextSelectedRowId = INVALID_ROW_ID;
            mNeedSync = false;

            checkFocus();
            requestLayout();
        }

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

在adapter.notifyDataSetChanged()時,便會調用裡面的onchangd()方法,通過mItemCount = getAdapter().getCount()獲取adapter中數據的數量,通過requestLayout()重新布局刷新界面。

這裡寫圖片描述

剛准備到此為止,忽然想起有件事還沒做完,現在,我們再簡單看下recyclerview與觀察者模式,不多說,直接撸代碼:

public static abstract class Adapter
  {
    private final RecyclerView.AdapterDataObservable mObservable;


    //部分代碼省略


    //接下來的代碼會瞬間讓你變得熟悉
    public final boolean hasObservers()
    {
      return this.mObservable.hasObservers();
    }

    public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
    {
      this.mObservable.registerObserver(observer);
    }

    public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
    {
      this.mObservable.unregisterObserver(observer);
    }

    public final void notifyDataSetChanged()
    {
      this.mObservable.notifyChanged();
    }

    //recyclerview可以單獨刷新item,原因就在此
    public final void notifyItemChanged(int position)
    {
      this.mObservable.notifyItemRangeChanged(position, 1);
    }

    public final void notifyItemRangeChanged(int positionStart, int itemCount)
    {
      this.mObservable.notifyItemRangeChanged(positionStart, itemCount);
    }

    public final void notifyItemInserted(int position)
    {
      this.mObservable.notifyItemRangeInserted(position, 1);
    }

    public final void notifyItemMoved(int fromPosition, int toPosition)
    {
      this.mObservable.notifyItemMoved(fromPosition, toPosition);
    }

    public final void notifyItemRangeInserted(int positionStart, int itemCount)
    {
      this.mObservable.notifyItemRangeInserted(positionStart, itemCount);
    }

    public final void notifyItemRemoved(int position)
    {
      this.mObservable.notifyItemRangeRemoved(position, 1);
    }

    public final void notifyItemRangeRemoved(int positionStart, int itemCount)
    {
      this.mObservable.notifyItemRangeRemoved(positionStart, itemCount);
    }
  }

recyclerview可以指定范圍刷新、插入刷新、移動刷新…一個控件,多套布局,多種玩法,這TM是不是有點違背了單一職責原則。。。

AdapterDataObservable

static class AdapterDataObservable extends Observable
  {
    public boolean hasObservers()
    {
      return !this.mObservers.isEmpty();
    }

    public void notifyChanged()
    {
      for (int i = this.mObservers.size() - 1; i >= 0; --i)
        ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged();
    }

    public void notifyItemRangeChanged(int positionStart, int itemCount)
    {
      for (int i = this.mObservers.size() - 1; i >= 0; --i)
        ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeChanged(positionStart, itemCount);
    }

    public void notifyItemRangeInserted(int positionStart, int itemCount)
    {
      for (int i = this.mObservers.size() - 1; i >= 0; --i)
        ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeInserted(positionStart, itemCount);
    }

    public void notifyItemRangeRemoved(int positionStart, int itemCount)
    {
      for (int i = this.mObservers.size() - 1; i >= 0; --i)
        ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeRemoved(positionStart, itemCount);
    }

    public void notifyItemMoved(int fromPosition, int toPosition)
    {
      for (int i = this.mObservers.size() - 1; i >= 0; --i)
        ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeMoved(fromPosition, toPosition, 1);
    }
  }

AdapterDataObserver

public static abstract class AdapterDataObserver
  {
    public void onChanged()
    {
    }

    public void onItemRangeChanged(int positionStart, int itemCount)
    {
    }

    public void onItemRangeInserted(int positionStart, int itemCount)
    {
    }

    public void onItemRangeRemoved(int positionStart, int itemCount)
    {
    }

    public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount)
    {
    }
  }

不做多余解釋,今天到此為止。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved