Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 觀察者模式在android 上的最佳實踐

觀察者模式在android 上的最佳實踐

編輯:關於Android編程

 

在上一篇文章中介紹了介紹了觀察者模式的定義和一些基本概念,觀察者模式在 android開發中應用還是非常廣泛的,例如android按鈕事件的監聽、廣播等等,在任何類似於新聞-訂閱的模式下面都可以使用。從某種意義上面來說android有點像JAVA EE的WEB頁面,在都需要提供View層用於進行操作,在多個頁面之間傳遞數據發送通知都是一件很麻煩的事情。

在android中從A頁面跳轉到B頁面,然後B頁面進行某些操作後需要通知A頁面去刷新數據,我們可以通過startActivityForResult來喚起B頁面,然後再B頁面結束後在A頁面重寫onActivityResult來接收返回結果從而來刷新頁面。但是如果跳轉路徑是這樣的A->B->C->.....,C或者C以後的頁面來刷新A,這個時候如果還是使用這種方法就會非常的棘手。使用這種方法可能會存在以下幾個弊端:

  1. 1.多個路徑或者多個事件的傳遞處理起來會非常困難。 2.數據更新不及時,往往需要用戶去等待,降低系統性能和用戶體驗。 3.代碼結構混亂,不易編碼和擴展。

    因此考慮使用觀察者模式去處理這個問題。

    一.需求確定

    在APP中我們有一些設置項目,我們希望在設置完了以後,在主頁面能夠立即響應,例如QQ的清空聊天記錄,我們希望設置了以後回到主頁面後會自動清理,有些人可能會認為這是一件很簡單的事情,認為回到主頁面直接讀緩存就好了,緩存裡面是什麼就是什麼,課時這種方案存在2個問題:

    • 什麼時候讀取緩存,是每次回到主頁面都去刷新嗎,這樣會太消耗資源,用戶體驗也不好。
    • 不能局部刷新數據。

      因此行功能和代碼結構上面來看我的需求主要有以下幾點:

      1. 1.能夠在某些頁面設置完了後直接通知其他監聽了這個事件的頁面立即刷新,而不需要用戶回到某些頁面的時候再刷新。 2.能夠區分是哪些事件通知的,從而針對不同的事件進行不同的處理。 3.能夠動態的擴展事件類型,可以讓調用者很快的注冊和監聽事件。

        二.系統設計

        從上一篇文章中我們知道一個完整的觀察者模式需要這些對象:

        1. Subject 抽象主題角色:也就是抽象的被觀察者對象,裡面保存了所有的觀察者對象引用列表,提供了注冊和反注冊的功能。 ConcreteSubject具體的主題角色:將有關狀態存入各ConcreteObserver對象 當它的狀態發送改變時,向它的各個觀察者發出通知 。 Observer 抽象觀察者 :為所有的具體觀察者定義一個接口,在得到通知時更新自己。 ConcreteObserver 具體觀察者:維護一個指向ConcreteObserver對象的引用 ,存儲有關狀態,這些狀態應與目標的狀態保持一致,實現Observer的更新接口是自身狀態與目標的狀態保持一致

          \

          針對在android我們需要設計一個一個抽象的BaseObserverActivity,讓所有的Activity頁面都去繼承它,從本質上來看繼承這個類的所有的Activity都是一個觀察者,然後再觀察者對象中去定義需要監聽是什麼類型的事件和根據對應的事件的處理。

          三.具體實現方案

          (1)EventSubjectInterface:抽象的主題角色實現

          /**
           * 抽象的主題角色
           * @author zhiwu_yan
           * @since 2015年04月06日
           * @version 1.0
           */
          public interface EventSubjectInterface {
              /**
               * 注冊觀察者
               * @param observer
               */
              public void registerObserver(EventObserver observer);
          
              /**
               * 反注冊觀察者
               * @param observer
               */
              public void removeObserver(EventObserver observer);
          
              /**
               * 通知注冊的觀察者進行數據或者UI的更新
               */
              public void notifyObserver(String eventType);
          }

          主要包括了觀察者的注冊方法和反注冊方法以及通知觀察者去更新UI的方法,我們來看看具體的實現。

          (2)EventSubject:具體的主題角色的實現

          /**
           * 具體的主題角色的實現,這裡用來監聽事件的發生,采用單例模式來實現
           * @author zhiwu_yan
           * @since 2015年04月06日
           * @version 1.0
           */
          public class EventSubject implements EventSubjectInterface{
          
              private List mEventObservers=new ArrayList();
              private static volatile EventSubject mEventSubject;
              private EventSubject(){
          
              }
          
              public synchronized static EventSubject getInstance(){
                  if(mEventSubject ==null){
                      mEventSubject =new EventSubject();
                  }
                  return mEventSubject;
              }
          
              @Override
              public void registerObserver(EventObserver observer) {
                  synchronized (mEventObservers){
                      if(observer!=null){
                          if(mEventObservers.contains(observer)){
                              return;
                          }
                          mEventObservers.add(observer);
                      }
                  }
          
              }
          
              @Override
              public void removeObserver(EventObserver observer) {
                  synchronized (mEventObservers){
                      int index = mEventObservers.indexOf(observer);
                      if (index >= 0) {
                          mEventObservers.remove(observer);
                      }
                  }
              }
          
              @Override
              public void notifyObserver(String eventType) {
                  if(mEventObservers!=null && mEventObservers.size()>0 && eventType!=null){
                      for(EventObserver observer:mEventObservers){
                          observer.dispatchChange(eventType);
                      }
                  }
          
              }
          }
          

          裡面要注意的地方是:使用單例模式來控制只有一個主題角色,裡面保存了所有的觀察者對象(EventObserver)列表,也就是護士手中的名單(見上一章),值得一提的是使用synchronized去控制多線程操作的問題。

          (3)EventObserverInterface:抽象觀察者對象

          /**
           * 觀察者接口
           * @author zhiwu_yan
           * @since 2015年04月06日
           * @version 1.0
           */
          public interface EventObserverInterface {
              /**
               * 根據事件進行數據或者UI的更新
               * @param eventType
               */
              public void dispatchChange(String eventType);
          }

          裡面只有一個根據事件類型來跟新UI的方法,我們看看具體的抽象觀察者。

          (4)EventObserver:具體的抽線觀察者

          /**
           * 用於更新UI,這裡執行更新UI的onChange方法
           * @author  zhiwu_yan
           * @since   2015年04月06
           * @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線程的,所以如果是耗時的操作需要另外開線程去處理。

          (5)前面已經說過了,Android裡面我們需要定義一個帶觀察者模式的BaseActivity用來給某些需要監聽的業務的Activity使用,這樣只要繼承了該Activity的都是一個具體的觀察者對象。

          /**
           * 帶有觀察者模式的Activity,本質上就是觀察者
           * @author  zhiwu_yan
           * @since  2015年04月6日 20:41
           * @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();
                      eventSubject.registerObserver(observer);
          
                  }
          
              }
          
              public void removeObserver(EventObserver observer) {
                  final String[] observerEventTypes=getObserverEventType();//獲取所有需要監聽的業務類型
                  if(observerEventTypes!=null && observerEventTypes.length>0){
                      final EventSubject eventSubject=EventSubject.getInstance();
                      eventSubject.removeObserver(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 mActivity;
                  public ActivityEventObserver(BaseObserverActivity activity){
                      super();
                      mActivity=new WeakReference(activity);
                  }
          
                  @Override
                  public void onChange(String eventType) {
                      BaseObserverActivity activity=mActivity.get();
                      if(activity!=null){
                          activity.onChange(eventType);
                      }
                  }
              }
          
          
          }

          另外我們需要定義一個可以動態擴展的事件類型:EventType

          /**
           * 所有的業務類型,在這裡寫,方便管理
           * @author zhiwu_yan
           * @since 2015年04月06日
           * @version 1.0
           */
          public class EventType {
          
              private static volatile EventType mEventType;
              private final static Set eventsTypes = new HashSet();
          
              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 zhiwu_yan
           * @since 2015年04月6日
           * @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);
                      }
                  });
              }
          
          
          }
          

           

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved