Android與設計模式——觀察者(Observer)模式

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述觀察者(Observer)模式的:

  觀察者模式是對象的行為模式,又叫發佈-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。
  觀察者模式定義瞭一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。

觀察者模式的結構
  一個軟件系統裡面包含瞭各種對象,就像一片欣欣向榮的森林充滿瞭各種生物一樣。在一片森林中,各種生物彼此依賴和約束,形成一個個生物鏈。一種生物的狀態變化會造成其他一些生物的相應行動,每一個生物都處於別的生物的互動之中。

  同樣,一個軟件系統常常要求在某一個對象的狀態發生變化的時候,某些其他的對象做出相應的改變。做到這一點的設計方案有很多,但是為瞭使系統能夠易於復用,應該選擇低耦合度的設計方案。減少對象之間的耦合有利於系統的復用,但是同時設計師需要使這些低耦合度的對象之間能夠維持行動的協調一致,保證高度的協作。觀察者模式是滿足這一要求的各種設計方案中最重要的一種。(上文源於網絡)

上圖是觀察者模式的基本模型,以通話狀態(CallState)為例,來分析手機通話狀態變化時,是怎麼傳遞這個事件的。

RegistrantList作為通知者,Registrant為觀察者,RegistrantList通知者提供瞭add、remove、notifyRegistrants來管理Registrant。vcD4KPHA+PHN0cm9uZz4xoaLNqLuw17TMrMrH1PXDtLG7vODM/bXEPzwvc3Ryb25nPjxicj4KPC9wPgo8cD5tQ2FsbFN0YXRlUmVnaXN0cmFudHPKx1JlZ2lzdHJhbnRMaXN0tcTSu7j2ttTP86Os1NpCYXNlQ29tbWFuZC5qYXZh1tDJ+rPJo6yyorbUUmVnaXN0cmFudExpc3TNqNaq1d+1xMztvNOhosm+s/3X9sHLt+LXsKO6PC9wPgo8cD48L3A+CjxwcmUgY2xhc3M9″brush:java;”>protected RegistrantList mCallStateRegistrants = new RegistrantList();

    public void registerForCallStateChanged(Handler h, int what, Object obj) {
        Registrant r = new Registrant (h, what, obj);//觀察者

        mCallStateRegistrants.add(r);
    }

    public void unregisterForCallStateChanged(Handler h) {
        mCallStateRegistrants.remove(h);
    }

registerForCallStateChanged中new瞭一個Registrant觀察者,將其加入到通知者中,來看Registrant是怎麼存儲的?

    public synchronized void
    add(Registrant r)
    {
        removeCleared();
        registrants.add(r);
    }

registrants是一個 ArrayList對象,管理Registrant其實就是對 ArrayList的操作。

registerForCallStateChanged是在GsmCallTracker.java的構造函數被調用的:

mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

this就是GsmCallTracker本身,GsmCallTracker繼承瞭Handler,此處,也就是說GsmCallTracker成為瞭觀察者,當CallState變化時mCallStateRegistrants應該通知GsmCallTracker。
當不需要監聽時,可註銷:

mCi.unregisterForCallStateChanged(this);

2、mCallStateRegistrants是怎麼通知GsmCallTracker CallState變化瞭?
在手機中,當通話狀態發生改變,Modem會向上傳遞消息,傳到framework時,被RIL.java接收處理:

            case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
                if (RILJ_LOGD) unsljLog(response);

                mCallStateRegistrants
                    .notifyRegistrants(new AsyncResult(null, null, null));
            break;

mCallStateRegistrants使用notifyRegistrants來通知它的所有觀察者:

    public /*synchronized*/ void
    notifyResult(Object result)
    {
        internalNotifyRegistrants (result, null);
    }

    private synchronized void
    internalNotifyRegistrants (Object result, Throwable exception)
    {
       for (int i = 0, s = registrants.size(); i < s ; i++) {
            Registrant  r = (Registrant) registrants.get(i);
            r.internalNotifyRegistrant(result, exception);
       }
    }

registrants就是之前存儲所有觀察者的ArrayList,遍歷,通知。

3、觀察者Registrant是怎麼響應處理通知的?

上面看到r.internalNotifyRegistrant(result, exception),觀察者知道callstate發生變化後,誰來真正處理?

Registrant部分代碼:

    internalNotifyRegistrant (Object result, Throwable exception)
    {
        Handler h = getHandler();

        if (h == null) {
            clear();
            /// M: Registrant Debug Log Enhancement
            Log.d("Registrant", "internalNotifyRegistrant(): Warning! Handler is null, it could be already GCed. ( what=" + what + ", userObj=" + userObj + ", result=" + result + ", exception=" + exception + " )");
        } else {
            Message msg = Message.obtain();

            msg.what = what;
            
            msg.obj = new AsyncResult(userObj, result, exception);
            
            h.sendMessage(msg);
        }
    }

Handler h = getHandler();獲得的Handler就是上文說到的this,也就是GsmCallTracker,what就是registerForCallStateChanged註冊時的第二個參數EVENT_CALL_STATE_CHANGE,這裡h.sendMessage也就是用的GsmCallTracker這個Handler發送的消息。我們都知道,Handler發送的消息都會被它自己的handleMessage()方法所接收處理。

於是,我們在GsmCallTracker的handleMessage()方法中看到:

            case EVENT_CALL_STATE_CHANGE:
                pollCallsWhenSafe();
            break;

具體做什麼操作,我們就不管瞭。。。

Android Telephony源碼中用到瞭大量的觀察者模式,理解觀察者模式,有助於我們閱讀源碼。

未完待續,有不對的地方,請指正。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *