美文网首页源码解析Android官方Api总结技术干货
读源码-用设计模式解析RecyclerView

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

作者: ed407c8602e0 | 来源:发表于2016-06-30 16:46 被阅读5175次

    前言

    读源码常常陷入繁复的细节中,结果看了半天,感觉自己懂了点什么,又好像什么也没弄懂。所以要带着目的性去看源码,理清主干部分的思路。本文的目的是梳理源码中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了。

    后记

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

    关注我(微信扫一扫)

    相关文章

      网友评论

      本文标题:读源码-用设计模式解析RecyclerView

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