通过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)
    {
    }
  }

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

时间: 2024-08-05 11:16:16

通过Android源码分析再探观察者模式(二)的相关文章

Android源码分析:Telephony部分–phone进程

Android源码分析:Telephony部分–phone进程红狼博客 com.android.phone进程 它就象个后台进程一样,开机即运行并一直存在.它的代码位于:packages/apps/Phone/src/com/android/phone 当有来电时,它会作出反应,如显示UI和铃声提示:当在通话过程中,它显示InCallScreen: 当要拨号时ITeleohony的接口调用最终到Phone进程,然后由它去与PhoneFactory创建的GSMPhone或CDMAPhone进行交互

Android源码分析之SharedPreferences

在Android的日常开发中,相信大家都用过SharedPreferences来保存用户的某些settings值.Shared Preferences 以键值对的形式存储私有的原生类型数据,这里的私有的是指只对你自己的app可见的,也就是说别的app是无法访问到的. 客户端代码为了使用它有2种方式,一种是通过Context#getSharedPreferences(String prefName, int mode)方法, 另一种是Activity自己的getPreferences(int mo

Android源码分析之模板方法模式

模式的定义 定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 使用场景 1.多个子类有公有的方法,并且逻辑基本相同时. 2.重要.复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现. 3.重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为. UML类图 角色介绍 AbstractClass : 抽象类,定义了一套算法框架. ConcreteClass1 :

Android源码分析--system_server进程分析

在上一篇博文中我们进行了有关Zygote进程的分析,我们知道Zygote进程创建了一个重要的进程–system_server进程后就进入了无限循环中,之后Android系统中的重要任务就交给了system_server进程,作为zygote的嫡长子进程,system_server进程的意义非凡,今天我们来分析一下system_server进程. 创建system_server进程 在ZygoteInit中main方法中,通过调用startSystemServer方法开启了system_serve

Android源码分析(十七)----init.rc文件添加脚本代码

一:init.rc文件修改 开机后运行一次: chmod 777 /system/bin/bt_config.sh service bt_config /system/bin/bt_config.sh class main user root group root oneshot 开机后等待android启动完成后再运行: service bt_config /system/bin/bt_config.sh class main user root group root disabled one

Cordova Android源码分析系列一(项目总览和CordovaActivity分析)

PhoneGap/Cordova是一个专业的移动应用开发框架,是一个全面的WEB APP开发的框架,提供了以WEB形式来访问终端设备的API的功能.这对于采用WEB APP进行开发者来说是个福音,这可以避免了原生开发的某些功能.Cordova 只是个原生外壳,app的内核是一个完整的webapp,需要调用的原生功能将以原生插件的形式实现,以暴露js接口的方式调用. Cordova Android项目是Cordova Android原生部分的Java代码实现,提供了Android原生代码和上层We

Android 源码分析工具

标 题: [原创]Android源码分析工具及方法作 者: MindMac时 间: 2014-01-02,09:32:35链 接: http://bbs.pediy.com/showthread.php?t=183278 在对 Android 源码进行分析时,如果有得力的工具辅助,会达到事半功倍的效果.本文介绍了一些在分析 Android 源码时使用的一些工具和方法,希望能够帮助到有需要的同学. Eclipse 在 Android 应用程序开发过程中,一般会使用 Eclipse,当然 Googl

Android源码分析:Telephony部分–GSMPhone

Android源码分析:Telephony部分–GSMPhone红狼博客 PhoneProxy/GSMPhone/CDMAPhone 如果说RILJ提供了工具或管道,那么Phone接口的子类及PhoneFactory则为packages/app/Phone这个应用程序进程使用RILJ这个工具或管道提供了极大的方便,它们一个管理整个整个手机的Telephony功能. GSMPhone和CDMAPhone实现了Phone中定义的接口.接口类Phone定义了一套API,这套API用于使用RILJ(见后

Android源码分析之SparseArray

本来接下来应该分析MessageQueue了,可是我这几天正好在实际开发中又再次用到了SparseArray(之前有用到过一次,那次只是 大概浏览了下源码,没做深入研究),于是在兴趣的推动下,花了些时间深入研究了下,趁着记忆还是新鲜的,就先在这里分析了. MessageQueue的分析应该会在本周末给出. 和以往一样,首先我们来看看关键字段和ctor: private static final Object DELETED = new Object(); private boolean mGar