00000026
观察者模式在 android开发中应用还是非常广泛的,例如android按钮事件的监听、广播等等,在任何类似于新闻-订阅的模式下面都可以使用。从某种意义上面来说android有点像JAVA EE的WEB页面,在都需要提供View层用于进行操作,在多个页面之间传递数据发送通知都是一件很麻烦的事情。
在android中从A页面跳转到B页面,然后B页面进行某些操作后需要通知A页面去刷新数据,我们可以通过startActivityForResult来唤起B页面,然后再B页面结束后在A页面重写onActivityResult来接收返回结果从而来刷新页面。但是如果跳转路径是这样的A->B->C->…..,C或者C以后的页面来刷新A,这个时候如果还是使用这种方法就会非常的棘手。使用这种方法可能会存在以下几个弊端:
因此考虑使用观察者模式去处理这个问题。
Android上观察者模式的实现有三种方式:Android本地广播、EventBus、观察者模式
一般认为本地广播是三种方式中消耗时间、空间最多的一种方式,但也是同 android 相性最好的方式。因为广播属于 android 四大组件之一,在 BroadcastReceiver 中的 onReceive 方法中可以获得 Context、Intent 参数。持有这两个参数便可以调用许多 android sdk 中的方法,这一优势另外两种方式很难弥补的,无论是 EventBus 还是观察者,需要获得 Context 的话,往往都需要进行复杂的参数传递或者是依赖注入。本地广播另外的一个优点是,许多系统级的事件都是使用广播来进行通知的,像常用的电量变化、网络状态变化、短信发送接收的状态等等。这就使得与 android 系统相关的通知,广播往往成了唯一的选择。
但这并不意味着 android 系统中的通知都应该使用广播,因为相对于其它的方式而言,广播是重量级的、消耗资源较多的方式。广播的优势体现在它与 android sdk 链接的更紧密,当我们需要同 android 交互的时候,广播提供的便捷性抵消掉了它过多的资源消耗。但是对于不需要同 android 交互或是只做很少的交互的时候,使用广播往往是一种浪费。
所以在不需要系统调用、进程通信的时候就可以不用广播,但是因为EventBus是基于反射的,利用方法名,来定义事件的,这会导致该类无法混淆,所以在一般情况下,使用观察者就可以达通知的目的。
在APP中我们有一些设置项目,我们希望在设置完了以后,在主页面能够立即响应,例如QQ的清空聊天记录,我们希望设置了以后回到主页面后会自动清理,有些人可能会认为这是一件很简单的事情,认为回到主页面直接读缓存就好了,缓存里面是什么就是什么,课时这种方案存在2个问题:
因此行功能和代码结构上面来看我的需求主要有以下几点:
一个完整的观察者模式需要这些对象:
针对在android我们需要设计一个一个抽象的BaseObserverActivity,让所有的Activity页面都去继承它,从本质上来看继承这个类的所有的Activity都是一个观察者,然后再观察者对象中去定义需要监听是什么类型的事件和根据对应的事件的处理。
/** * 抽象的主题角色 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public interface EventSubjectInterface { /** * 注册观察者 * @param observer */ public void registerObserver(String eventType,EventObserver observer); /** * 反注册观察者 * @param observer */ public void removeObserver(String eventType,EventObserver observer); /** * 通知注册的观察者进行数据或者UI的更新 */ public void notifyObserver(String eventType); }
主要包括了观察者的注册方法和反注册方法以及通知观察者去更新UI的方法,我们来看看具体的实现。
/** * 具体的主题角色的实现,这里用来监听事件的发生,采用单例模式来实现 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public class EventSubject implements EventSubjectInterface{ private static final String TAG="EventSubject"; private Map<String,ArrayList<EventObserver>> mEventObservers=new HashMap<String,ArrayList<EventObserver>>(); private static volatile EventSubject mEventSubject; private EventSubject(){ } public synchronized static EventSubject getInstance(){ if(mEventSubject ==null){ mEventSubject =new EventSubject(); } return mEventSubject; } @Override public void registerObserver(String eventType,EventObserver observer) { synchronized (mEventObservers){ ArrayList<EventObserver> eventObservers = mEventObservers.get(eventType); if (eventObservers == null) { eventObservers = new ArrayList<EventObserver>(); mEventObservers.put(eventType, eventObservers); } if(eventObservers.contains(observer)) { return; } eventObservers.add(observer); } } @Override public void removeObserver(String eventType,EventObserver observer) { synchronized (mEventObservers){ int index = mEventObservers.get(eventType).indexOf(observer); if (index >= 0) { mEventObservers.remove(observer); } } } @Override public void notifyObserver(String eventType) { if(mEventObservers!=null && mEventObservers.size()>0 && eventType!=null){ ArrayList<EventObserver> eventObservers=mEventObservers.get(eventType); if(eventObservers!=null){ for(EventObserver observer:eventObservers){ observer.dispatchChange(eventType); } }else{ Log.e(TAG,"eventObservers is null,"+eventType+" may be not register"); } } } }
里面要注意的地方是:使用单例模式来控制只有一个主题角色,里面保存了所有的观察者对象(EventObserver)列表,也就是手中的名单,值得一提的是使用synchronized去控制多线程操作的问题。
/** * 观察者接口 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public interface EventObserverInterface { /** * 根据事件进行数据或者UI的更新 * @param eventType */ public void dispatchChange(String eventType); }
里面只有一个根据事件类型来跟新UI的方法,我们看看具体的抽象观察者。
/** * 用于更新UI,这里执行更新UI的onChange方法 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public abstract class EventObserver implements EventObserverInterface { private Handler mHandler; public EventObserver(){ mHandler=new Handler(Looper.getMainLooper()); } public abstract void onChange(String eventType); @Override public void dispatchChange(String eventType){ mHandler.post(new NotificationRunnable(eventType)); } private final class NotificationRunnable implements Runnable{ private String mEventType; public NotificationRunnable(String eventType){ this.mEventType=eventType; } @Override public void run() { EventObserver.this.onChange(mEventType); } } }
我们定义了一个抽象的onChange方法交给子类去实现,这个方法就是用来处理对应的事件类型,比如需要刷新数据等等。因为mHandler.post来跟新UI线程的,所以如果是耗时的操作需要另外开线程去处理。
/** * 带有观察者模式的Activity,本质上就是观察者 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public abstract class BaseObserverActivity extends ActionBarActivity { private ActivityEventObserver mActivityEventObserver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityEventObserver=new ActivityEventObserver(this); registerObserver(mActivityEventObserver); } @Override protected void onDestroy() { super.onDestroy(); removeObserver(mActivityEventObserver); } public void registerObserver(EventObserver observer) { final String[] observerEventTypes=getObserverEventType();//获取所有需要监听的业务类型 if(observerEventTypes!=null && observerEventTypes.length>0){ final EventSubject eventSubject=EventSubject.getInstance(); for(String eventType:observerEventTypes){ eventSubject.registerObserver(eventType,observer); } } } public void removeObserver(EventObserver observer) { final String[] observerEventTypes=getObserverEventType();//获取所有需要监听的业务类型 if(observerEventTypes!=null && observerEventTypes.length>0){ final EventSubject eventSubject=EventSubject.getInstance(); for(String eventType:observerEventTypes){ eventSubject.removeObserver(eventType, observer); } } } /** * 该方法会在具体的观察者对象中调用,可以根据事件的类型来更新对应的UI,这个方法在UI线程中被调用, * 所以在该方法中不能进行耗时操作,可以另外开线程 * @param eventType 事件类型 */ protected abstract void onChange(String eventType); /** * 通过这个方法来告诉具体的观察者需要监听的业务类型 * @return */ protected abstract String[] getObserverEventType(); private static class ActivityEventObserver extends EventObserver { //添加弱引用,防止对象不能被回收 private final WeakReference<BaseObserverActivity> mActivity; public ActivityEventObserver(BaseObserverActivity activity){ super(); mActivity=new WeakReference<BaseObserverActivity>(activity); } @Override public void onChange(String eventType) { BaseObserverActivity activity=mActivity.get(); if(activity!=null){ activity.onChange(eventType); } } } }
另外我们需要定义一个可以动态扩展的事件类型:EventType
/** * 所有的业务类型,在这里写,方便管理 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public class EventType { private static volatile EventType mEventType; private final static Set<String> eventsTypes = new HashSet<String>(); public final static String UPDATE_MAIN="com.updateMain"; public final static String UPDATE_Text="com.updateText"; private EventType(){ eventsTypes.add(UPDATE_MAIN); eventsTypes.add(UPDATE_Text); } public static EventType getInstance(){ if(mEventType==null){ mEventType=new EventType(); } return mEventType; } public boolean contains(String eventType){ return eventsTypes.contains(eventType); } }
我这里主要定义个2个事件类型,如果需要你可以定义N个事件类型,只要把你需要定义的事件添加到事件类表里面去就可以了。
我们在通知某个页面需要更新的时候只需呀调用如下方法:
EventSubject eventSubject=EventSubject.getInstance(); EventType eventTypes=EventType.getInstance(); if(eventTypes.contains(eventType)){ eventSubject.notifyObserver(eventType); }
为了便于管理我们也新建一个工具类:
/** * 通知中心,用来通知更新数据或者UI,采用单例模式 * @author xxx * @since 2016年08月03日 * @version 1.0 */ public class Notify { private static volatile Notify mNotify; private Notify(){ } public static Notify getInstance(){ if(mNotify==null){ mNotify=new Notify(); } return mNotify; } public void NotifyActivity(String eventType){ EventSubject eventSubject=EventSubject.getInstance(); EventType eventTypes=EventType.getInstance(); if(eventTypes.contains(eventType)){ eventSubject.notifyObserver(eventType); } } }
到这里基本的框架就完成,我们看看怎么使用。
定义一个A页面:MainActivity。这个页面是一个观察者,需要监听来自其他页面的一些通知,一旦有修改就根据对应的的事件来做出不同的处理:
public class MainActivity extends BaseObserverActivity { private TextView mLableTv; private ImageView mPicIv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLableTv=(TextView) findViewById(R.id.label_tv); mPicIv=(ImageView) findViewById(R.id.pic_iv); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id){ case R.id.go_other_activity: goActivity(OtherActivity.class); return true; } return super.onOptionsItemSelected(item); } private void goActivity(Class<?> activity){ Intent intent=new Intent(this,activity); startActivity(intent); } @Override protected void onChange(String eventType) { if(EventType.UPDATE_MAIN==eventType){ mPicIv.setImageResource(R.mipmap.pic_two); }else if(EventType.UPDATE_Text==eventType){ mLableTv.setText("图片被更新"); } } @Override protected String[] getObserverEventType() { return new String[]{ EventType.UPDATE_MAIN, EventType.UPDATE_Text }; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); startActivityForResult(); } }
主要看一下:onChange 方法:根据事件类型来更新不同的图片,而在getObserverEventType()中我们定义了该观察者需要观察的业务类型,其它业务类型则会被忽略。
我们的B页面:也就是发出通知的页面,APP上面的设置页面,唯一的作用就是通知观察者:
public class OtherActivity extends ActionBarActivity { private Button mUpdateBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.other_activity); mUpdateBtn=(Button) findViewById(R.id.update_edit_btn); mUpdateBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Notify.getInstance().NotifyActivity(EventType.UPDATE_Text); Notify.getInstance().NotifyActivity(EventType.UPDATE_MAIN); } }); } }
好,大功告成!