美文网首页
通过Android源码分析再探观察者模式(二)

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

作者: a9b430bb6daa | 来源:发表于2017-07-24 18:21 被阅读27次

    接着上篇文章,现在在通过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源码分析再探观察者模式(二)

          本文链接:https://www.haomeiwen.com/subject/tnabkxtx.html