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

來源:互聯網
上載者:User

觀察者模式在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);            }        });    }}

           

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.