通过Android源码分析再探观察者模式

Posted 下一个丶奇迹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过Android源码分析再探观察者模式相关的知识,希望对你有一定的参考价值。

接着上篇文章,现在在通过android实际开发和源码再探观察者模式,listview是我们日常开发中必用的控件,虽然之前就有listview的替代品(recyclerview),现在不去深究两个控件到底谁好谁差,但有一点需要强调下,它们之间有一大相同点,都是通过观察者模式去实现数据更新。

首先,我们先去看一个简单的例子

/**
 * 
 * created by zero on2016-6-1
 * 
 * 通过listview再探观察者模式
 * 
 */
public class MainActivity extends Activity
{
    private ListView lv_simple;
    private ArrayAdapter<String> 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<String>(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的源码

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

先看到这里,通过上述的注释可知道,当observable发生改变的时候,便会通知DataSetObserver对象的列表发生对应的改变。现在,好像明白了点什么,但是我们没有找到容器,我们继续追查下去,接下来,我们再看下DataSetObserver的父类,如下:

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

    /**
     * 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<DataSetObserver>
public abstract class Observable<T> {    
    protected final ArrayList<T> mObservers = new ArrayList<T>();
    }

现在一目了然了,我们找到了容器了,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<VH extends RecyclerView.ViewHolder>
  {
    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<RecyclerView.AdapterDataObserver>
  {
    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)
    {
    }
  }

不做多余解释,今天到此为止。

以上是关于通过Android源码分析再探观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

Android--再探MVP模式

redux源码分析通过原生js手写实现redux来理解redux的数据响应

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

当观察者模式和回调机制遇上Android源码