读源码-用设计模式解析RecyclerView

96
作者 wolearn
2016.06.30 16:46* 字数 1334

前言

读源码常常陷入繁复的细节中,结果看了半天,感觉自己懂了点什么,又好像什么也没弄懂。所以要带着目的性去看源码,理清主干部分的思路。本文的目的是梳理源码中RecyclerView怎么根据数据源的变更,让Item更新UI的。由于要以观察者模式为解析刀,所以先来看看观察者模式的前世今生。还是那句话:希望能简单点把问题说清楚,避免高大上的定义,虚无缥缈的遣词造句。

1

JDK源码中的观察者模式

观察者模式可以简单理解成定报纸,如果你订阅了某报纸,等有新的报纸时,该报就会把新的报纸投递给所有的订阅报纸的人。即你订阅,有更新就会给到你。这个订阅者和报纸之间就构成了观察者和被观察者的关系。JDK有2个类是帮助开发者快速实现观察者模式的—Observable(抽象被观察者)Observer(抽象观察者)

public interface Observer {
    void update(Observable o, Object arg);
}

发现JDK实现的抽象观察者很简单,就是个接口,有个叫update的方法。这个方法是用来接收被观察者发过来的新消息,消息的内容是参数arge。

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

梳理下这个类

  • 有个Vector<Observer>类型的集合用来存放观察者对象,有相关的方法addObserver,deleteObserver来操作删减集合中的对象。即订阅了这个被观察者,我就调用addObserver添加一个观察者。
  • 变参的通知方法notifyObservers,关键点就是取出集合中的观察者对象,然后用一个for循环不断的调用每个订阅者的update方法,通知他们有新的消息。
  • 全局变量changed,就是一个标识位,看数据是否有更新

是不是突然感觉好简单,想徒手撸个源码一样的观察者模式?

2

用JDK提供的类型实现观察者模式

既然了解了原理,立即手痒难耐的撸个例子。先来一个报纸(NewspaperObservable)

public class NewspaperObservable extends Observable{
    public void notifyAllMan(String info)
    {
        setChanged();
        notifyObservers(info);
    }
}

再来个定报纸的人(ManObserver),收到新报纸,就打印报纸的内容

public class ManObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.print(arg);
    }
}

最后试试,订报纸的人能不能及时收到新报纸。

public class Test {
    public static void main(String[] arge)
    {
        NewspaperObservable newspaperObservable = new NewspaperObservable();
        newspaperObservable.addObserver(new ManObserver());
        newspaperObservable.addObserver(new ManObserver());
        newspaperObservable.notifyAllMan("have a new paper");
    }
}

3

Android源码中的观察者模式

Android Framework没有直接使用java中的观察者模式,而是有自己的实现,看懂了第一节中关键几点,相信这很简单。

public abstract class Observable<T> {
    protected final ArrayList<T> mObservers = new ArrayList<T>();

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

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

    public void unregisterAll() {
        synchronized(mObservers) {
            mObservers.clear();
        }
    }
}

Android中的被观察者维护了一个观察者的集合,通过registerObserver,unregisterObserver方法管理订阅者,跟JDK中的做法如出一辙。可是最最重要的通知订阅者更新的方法呢??一脸懵逼的接着往下看。。。

public class DataSetObservable extends Observable<DataSetObserver> {
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}

发现类DataSetObservable继承了Observable,并实现了通知方法。那为什么不在Observable中实现这些方法呢,跟JDK中的实现一样。我的理解是,这里的Observable使用了泛型,而交给子类去实现通知方法,子类可以灵活的定义观察者类型,并使用观察者中的方法。所以DataSetObservable已经深入到业务中了。观察者变化不大。

public abstract class DataSetObserver {
    public void onChanged() {
        // Do nothing
    }

    public void onInvalidated() {
        // Do nothing
    }
}

4

RecyclerView源码

终于可以开始我们最原始的问题:源码中RecyclerView怎么根据数据源的变更,让Item更新UI的?秘密应该在RecyclerView,RecyclerView.Adapter这两个类中。根据我们的使用经验,有重大嫌疑的方法有RecyclerView.setAdapter(),Adapter.notifyDataSetChanged()。。。本文正式从技术文变推理文。。。RecyclerView源码一看11090行,再看RecyclerView.Adapter少很多哇(内部类当然的),果断放上源码

public static abstract class Adapter<VH extends ViewHolder> {
        private final AdapterDataObservable mObservable = new AdapterDataObservable();
        private boolean mHasStableIds = false;

        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

        public abstract void onBindViewHolder(VH holder, int position);

        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
            onBindViewHolder(holder, position);
        }

        public final VH createViewHolder(ViewGroup parent, int viewType) {
            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
            final VH holder = onCreateViewHolder(parent, viewType);
            holder.mItemViewType = viewType;
            TraceCompat.endSection();
            return holder;
        }

        public final void bindViewHolder(VH holder, int position) {
            holder.mPosition = position;
            if (hasStableIds()) {
                holder.mItemId = getItemId(position);
            }
            holder.setFlags(ViewHolder.FLAG_BOUND,
                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
            holder.clearPayload();
            TraceCompat.endSection();
        }

        public int getItemViewType(int position) {
            return 0;
        }

        public void setHasStableIds(boolean hasStableIds) {
            if (hasObservers()) {
                throw new IllegalStateException("Cannot change whether this adapter has " +
                        "stable IDs while the adapter has registered observers.");
            }
            mHasStableIds = hasStableIds;
        }

        public long getItemId(int position) {
            return NO_ID;
        }

        public abstract int getItemCount();

        public final boolean hasStableIds() {
            return mHasStableIds;
        }

        public void onViewRecycled(VH holder) {
        }

        public boolean onFailedToRecycleView(VH holder) {
            return false;
        }

        public void onViewAttachedToWindow(VH holder) {
        }

        public void onViewDetachedFromWindow(VH holder) {
        }

        public final boolean hasObservers() {
            return mObservable.hasObservers();
        }

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

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

        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        }

        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        }

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

        public final void notifyItemChanged(int position) {
            mObservable.notifyItemRangeChanged(position, 1);
        }

        public final void notifyItemChanged(int position, Object payload) {
            mObservable.notifyItemRangeChanged(position, 1, payload);
        }

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

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

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

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

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

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

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

一眼就看到了全局变量mObservable赤果果的站在第一行,然后看到方法registerAdapterDataObserver,unregisterAdapterDataObserver,我可以大胆的推测每个Item都是数据变更的观察者,而被观察者赫然就是AdapterDataObservable。而后面的代码

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

        public final void notifyItemChanged(int position) {
            mObservable.notifyItemRangeChanged(position, 1);
        }

        public final void notifyItemChanged(int position, Object payload) {
            mObservable.notifyItemRangeChanged(position, 1, payload);
        }

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

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

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

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

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

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

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

这些通知数据变更的方法,包括节点的更新,全局的更新,无一例外的是由被观察者AdapterDataObservable发送更新通知后完成。那么下面只需要了解以下问题,就能回答最初的问题。

  1. AdapterDataObserver 和 AdapterDataObservable什么时候完成绑定的?
  2. AdapterDataObserver 怎么更新UI的?

那么是时候看RecyclerView.setAdapter()方法了。

public void setAdapter(Adapter adapter) {
        // bail out if layout is frozen
        setLayoutFrozen(false);
        setAdapterInternal(adapter, false, true);
        requestLayout();
    }

接着看setAdapterInternal()

private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        if (!compatibleWithPrevious || removeAndRecycleViews) {
            // end all running animations
            if (mItemAnimator != null) {
                mItemAnimator.endAnimations();
            }
            // Since animations are ended, mLayout.children should be equal to
            // recyclerView.children. This may not be true if item animator's end does not work as
            // expected. (e.g. not release children instantly). It is safer to use mLayout's child
            // count.
            if (mLayout != null) {
                mLayout.removeAndRecycleAllViews(mRecycler);
                mLayout.removeAndRecycleScrapInt(mRecycler);
            }
            // we should clear it here before adapters are swapped to ensure correct callbacks.
            mRecycler.clear();
        }
        mAdapterHelper.reset();
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }
        if (mLayout != null) {
            mLayout.onAdapterChanged(oldAdapter, mAdapter);
        }
        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
        mState.mStructureChanged = true;
        markKnownViewsInvalid();
    }

代码中很明确的调用了

adapter.registerAdapterDataObserver(mObserver);

完成了绑定。而mObserver是RecyclerViewDataObserver类型,RecyclerViewDataObserver继承了AdapterDataObserver 。那么更新UI的方法应该在RecyclerViewDataObserver中。

private class RecyclerViewDataObserver extends AdapterDataObserver {
        @Override
        public void onChanged() {
            assertNotInLayoutOrScroll(null);
            if (mAdapter.hasStableIds()) {
                // TODO Determine what actually changed.
                // This is more important to implement now since this callback will disable all
                // animations because we cannot rely on positions.
                mState.mStructureChanged = true;
                setDataSetChangedAfterLayout();
            } else {
                mState.mStructureChanged = true;
                setDataSetChangedAfterLayout();
            }
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();
            }
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            assertNotInLayoutOrScroll(null);
            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
                triggerUpdateProcessor();
            }
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            assertNotInLayoutOrScroll(null);
            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
                triggerUpdateProcessor();
            }
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            assertNotInLayoutOrScroll(null);
            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
                triggerUpdateProcessor();
            }
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            assertNotInLayoutOrScroll(null);
            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
                triggerUpdateProcessor();
            }
        }

        void triggerUpdateProcessor() {
            if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
            } else {
                mAdapterUpdateDuringMeasure = true;
                requestLayout();
            }
        }
    }

最后看AdapterDataObservable发送通知的方法,通过这些notify方法通知观察者更新UI。

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

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

        public void notifyItemRangeChanged(int positionStart, int itemCount) {
            notifyItemRangeChanged(positionStart, itemCount, null);
        }

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

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

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

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

RecyclerViewDataObserver 接到通知就妥妥的更新UI了。

后记

基本上回答了文章开始的问题,没有在源码中迷路。。。。喜欢的童鞋帮忙戳喜欢,有问题也可以评论。

关注我(微信扫一扫)


读源码