Monday, January 18, 2021

Android Observer Pattern

 

应用场景

  • 当一个对象的改变需要通知其它对象改变时,而且它不知道具体有多少个对象有待改变时。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

  • 解除观察者与主题之间的耦合。让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
  • 易于扩展,对同一主题新增观察者时无需修改原有代码。

缺点

  • 依赖关系并未完全解除,抽象主题仍然依赖抽象观察者。
  • 使用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
Android 中的源码分析 1

===================================================================
        Button button = (Button) findViewById(R.id.button);
        //注册观察者
        button.setOnClickListener(new View.OnClickListener() {
            //观察者实现
            @Override
            public void onClick(View arg0) {
                Log.d("test", "Click button ");
            }
        });
===================================================================

上面代码中,button就是具体的主题,也就是被观察者;

new 出来的 View.OnClickListenerd 对象就是具体的观察者;

OnClickListener 实际上就是个接口,也就是抽象观察者;通过 setOnClickListener 把观察者注册到被观察者中。

一旦 button 捕获的点击事件,即状态发生变化的时候,就会通过回调注册的 OnClickListener 观察者的 onClick 方法会来通知观察者,Button 状态发生变化。。

==================================================================
    public interface OnClickListener {//抽象观察者

        void onClick(View v);//只有onClick这个方法
    }

    //注册观察者
    public void setOnClickListener(@Nullable View.OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);//设置为可点击
        }
        getListenerInfo().mOnClickListener = l;//把传入的 OnClickListener 对象赋值给了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener对象的引用
    }

    ListenerInfo getListenerInfo() {//返回ListenerInfo对象,这里是一个单例模式
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

    public boolean performClick() {//执行点击事件
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);//执行onClick方法,li.mOnClickListener即OnClickListener对象
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }
=======================================================================

Android 中的源码分析 2: Adapter的notifyDataSetChanged()方法

当我们使用 ListView 时,需要更新数据时我们就会调用 Adapter 的 notifyDataSetChanged() 方法,那么我们来看看 notifyDataSetChanged() 的实现原理,这个方法是定义在 BaseAdaper 中,具体代码如下:

=======================================================================
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
     //数据集被观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    //注册观察者
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    //注销观察者
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    //数据集改变时,通知所有观察者
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}
    //其他代码略

=======================================================================

由上面的代码可以看出BaseAdapter实际上就是使用了观察者模式,BaseAdapter就是具体的被观察者。接下来看看 mDataSetObservable.notifyChanged()的实现

=======================================================================

//数据集被观察者
public class DataSetObservable extends Observable<DataSetObserver> {
   
    public void notifyChanged() {
        synchronized(mObservers) {
            //遍历所有观察者,并调用他们的onChanged()方法
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    //其他代码略
}

=======================================================================

由上面的代码可以看出BaseAdapter实际上就是使用了观察者模式,BaseAdapter就是具体的被观察者。接下来看看 mDataSetObservable.notifyChanged()的实现:

=======================================================================
//数据集被观察者
public class DataSetObservable extends Observable<DataSetObserver> {
   
    public void notifyChanged() {
        synchronized(mObservers) {
            //遍历所有观察者,并调用他们的onChanged()方法
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    //其他代码略
}
=======================================================================

现在我们看到了有观察者的影子,那么这些观察者是从哪里来的呢?实际上这些观察者是在ListView通过setAdaper()设置Adaper时产生的:

=======================================================================

public class ListView extends AbsListView {
    //其他代码略
    
    public void setAdapter(ListAdapter adapter) {
        //如果已存在Adapter,先注销该Adapter的观察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        
        //其他代码略
        
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();//获取Adapter中的数据的数量
            checkFocus();

            mDataSetObserver = new AdapterDataSetObserver();//创建一个数据集观察者
            mAdapter.registerDataSetObserver(mDataSetObserver);//注册观察者

           //其他代码略
        } 
    }
}

=======================================================================

从上面的代码可以看到,观察者有了,那么这个观察者主要是干什么的呢?

=======================================================================

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();//调用父类的onChanged()方法
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }

       //其他代码略
    }

=======================================================================

AdapterDataSetObserver类中的onChanged()方法没看出啥,继续看他父类的onChanged()方法:

=======================================================================

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;
        //观察者的核心实现
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();//获取Adapter中的数据的数量
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //重新布局
            requestLayout();
        }

       //其他代码略
    }

=======================================================================

最终就是在AdapterDataSetObserver这个类里面的onChanged()方法中实现了布局的更新。

Reference:
https://www.jianshu.com/p/8f32da74cd8b

No comments:

Post a Comment

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能