Android設計模式之觀察者模式


Android設計模式之觀察者模式

觀察者模式(Observer Pattern)

  • 定義對象間的一種一個(Observable)對多(Observer)的依賴關系,當一個對象的狀態發送改變時,所以依賴於它的對象都得到通知並被自動更新。

  • 當然,MVC只是Observer模式的一個實例。

  • Observer模式要解決的問題為:建立一個一(Observable)對多(Observer)的依賴關系,並且做到當“一”變化的時候,依賴這個“一”的多也能夠同步改變。最常見的一個例子就是:對同一組數據進行統計分析時候,我們希望能夠提供多種形式的表示(例如以表格進行統計顯示、柱狀圖統計顯示、百分比統計顯示等)。這些表示都依賴於同一組數據,我們當然需要當數據改變的時候,所有的統計的顯示都能夠同時改變。Observer模式就是解決了這一個問題。

  • 因此我們知道,觀察者模式中主要有觀察者和被觀察者2個對象,而在觀察模式中Observable表示被觀察者,由於這個對象是一個抽象類,所以只能被繼承。而Observer表示觀察者,它並不是一個類而是一個接口,所以觀察者可以有多個,實現了該接口的類都屬於觀察者。 當然有人會問既然被觀察者是一個抽象類而觀察者是一個接口,那么是否有一個類即繼承Observable類又實現Observer接口呢,這個自然可以,觀察者當然也可以同時是被觀察者,被觀察者同樣可以是觀察者,在這里完全不沖突。

  • 當某一個我們需要被觀察的對象繼承自Observable類時,就表示該類可以被很多觀察者(實現了Observer)觀察,其實意思就是說這個對象也就是被觀察者“上電視了”,電視前的你也就是觀察者可以看,(對象發生變化)電視里被觀察者做了別的動作,那么電視前的所有人看到的也就變了,在同一時間改變所有觀眾看到的畫面。

觀察者模式的適用性:

  1. 當一個抽象模型有兩個方面,其中一個方面依賴於另一方面,將這兩者封裝成獨立的對象中以使它們可以各自獨立的改變和服用
  2. 當對一個對象的改變需要同時改變其他對象,而不知道具體有多少對象有待改變
  3. 當一個對象必須通知其它對象,而它又不能假定其它對象是誰

觀察者模式的參與者:

  1. Observable(目標也叫被觀察者)
    • 目標知道它的觀察者,可以有任意多個觀察者觀察同一個目標
    • 提供注冊和刪除觀察者對象的接口
  2. Observer(觀察者)
    • 為那些在目標發生改變時需獲得通知的對象定義個更新的接口
  3. ConcreteObservable(具體目標)
    • 將有關狀態存入各ConcreteObserver對象
    • 當它的狀態發送改變時,向它的各個觀察者發出通知
  4. ConcreteObserver(具體觀察者)
    • 維護一個指向ConcreteObserver對象的引用
    • 存儲有關狀態,這些狀態應與目標的狀態保持一致
    • 實現Observer的更新接口是自身狀態與目標的狀態保持一致

下面我們用一個例子來展示Android觀察者模式

例如:MyPerson是被觀察者:

package com.shijiacheng.observerpattern;

import java.util.Observable;

public class MyPerson extends Observable {

    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        setChanged();
        notifyObservers();
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        setChanged();
        notifyObservers();
    }

    @Override
    public String toString() {
        return "MyPerson [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
}
  • 注意到:setChanged();notifyObservers();多了這兩句調用,通過setChanged();告知數據改變,通過notifyObservers();發送信號通知觀察者。

MyObserver是觀察者:

package com.shijiacheng.observerpattern;

import java.util.Observable;
import java.util.Observer;

public class MyObserver implements Observer {

    private int id;
    private MyPerson myPerson;

    public MyObserver(int id) {
        System.out.println("我是觀察者---->" + id);
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public MyPerson getMyPerson() {
        return myPerson;
    }

    public void setMyPerson(MyPerson myPerson) {
        this.myPerson = myPerson;
    }

    @Override
    public void update(Observable observable, Object data) {
        System.out.println("觀察者---->" + id + "得到更新");
        this.myPerson = (MyPerson) observable;
        System.out.println(((MyPerson) observable).toString());
    }

}
  • 這里我們看到,實現了Observer,只有一個update方法,那么這個方法什么時候被調用呢,很顯然,在上面我們有通知的信號,那么這里就是接受到信號后執行的動作。

MainActivity主函數:

package com.shijiacheng.observerpattern;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ListActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;

public class MainActivity extends ListActivity {

    private Button btnAddObserver;
    private Button btnChangeData;
    private MyPerson observable;
    private MyObserver myObserver;
    private List<MyObserver> myObservers;
    private ListView listview;

    private int i;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            MyListAdapter myListAdapter = new MyListAdapter(MainActivity.this,
                    myObservers);
            listview.setAdapter(myListAdapter);

        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnAddObserver = (Button) findViewById(R.id.btn_add_observer);
        btnChangeData = (Button) findViewById(R.id.btn_change_data);
        listview = getListView();

        observable = new MyPerson();
        myObservers = new ArrayList<MyObserver>();

        btnAddObserver.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                myObserver = new MyObserver(i);
                i++;
                observable.addObserver(myObserver);
                myObservers.add(myObserver);
                handler.sendEmptyMessage(0);
            }
        });

        btnChangeData.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                observable.setName("a" + i);
                observable.setAge(10 + i);
                observable.setSex("男" + i);
                handler.sendEmptyMessage(0);
            }
        });

    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        observable.deleteObserver(myObserver);
    }
}
  • btn_add_observer這個button是用來添加觀察者的,而我們看到myObservers.add(myObserver);這樣一句,它的意思就是為被觀察者添加觀察者的。btn_change_data這個button用來改變數據用於測試。

最后,看一下實例的效果圖和輸出log:

  • 當點擊一次“添加觀察者”:

這里寫圖片描述

這里寫圖片描述

  • 點擊一次“改變數據”:

這里寫圖片描述

這里寫圖片描述

  • 然后點擊三次“添加觀察者”:

這里寫圖片描述

  • 點擊三次“改變數據”:

這里寫圖片描述

這里寫圖片描述

源碼下載地址:http://download.csdn.net/detail/u012771445/9367896


下面我們展示一個在Android應用開發中應用觀察者模式的實例:

Android 項目開發實戰:短信驗證碼自動填寫

這里寫圖片描述

ContentObserver

  • 內容觀察者,目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理,它類似於數據庫技術中的觸發器(Trigger),當 ContentObserver 所觀察的Uri發生變化時,便會觸發它。

觀察特定Uri的步驟如下:

  1. 創建我們特定的 ContentObserver 派生類,必須重載父類構造方法,必須重載 onChange() 方法去處理回調后的功能實現。
  2. 利用 context.getContentResolover() 獲得 ContentResolove 對象,接着調用 registerContentObserver() 方法去注冊內容觀察者。
  3. 由於 ContentObserver 的生命周期不同步於 Activity 和 Service 等,因此,在不需要時,需要手動的調用 unregisterContentObserver() 去取消注冊。

SmsObserver.java

package com.shijiacheng.verificationcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;

public class SmsObserver extends ContentObserver {

    private Context mContext;
    private Handler mHandler;

    public SmsObserver(Context context, Handler handler) {
        super(handler);
        mContext = context;
        mHandler = handler;
    }

    /**
     * 接收到短信時調用此方法
     */
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);

        Log.d("DEBUG", "SMS has changed!");
        Log.d("DEBUG", uri.toString());

        if (uri.toString().equals("content://sms/raw")) {
            return;
        }

        Uri inboxUri = Uri.parse("content://sms/inbox");
        Cursor cursor = mContext.getContentResolver().query(inboxUri, null,
                null, null, "date desc");
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                String address = cursor.getString(cursor
                        .getColumnIndex("address"));
                String body = cursor.getString(cursor.getColumnIndex("body"));
                Log.d("DEBUG", "發件人為:" + address + " " + "短信內容為:" + body);

                // if (!address.equals("你的公司的手機號")) {
                // return;
                // }

                Pattern pattern = Pattern.compile("(\\d{6})");//正則表達式
                Matcher matcher = pattern.matcher(body);
                if (matcher.find()) {
                    String code = matcher.group(0);
                    Log.d("DEBUG", "code is" + code);

                    mHandler.obtainMessage(MainActivity.MSG_RECEIVED_CODE, code)
                            .sendToTarget();
                }
            }
        }

    }

}

MainActivity.java

package com.shijiacheng.verificationcode;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.widget.EditText;

public class MainActivity extends Activity {
    public static final int MSG_RECEIVED_CODE = 1;

    private EditText etVerficationCode;

    private SmsObserver mObserver;
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (msg.what == MSG_RECEIVED_CODE) {
                String code = (String) msg.obj;
                etVerficationCode.setText(code);
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etVerficationCode = (EditText) findViewById(R.id.et_verification_code);

        mObserver = new SmsObserver(MainActivity.this, mHandler);
        Uri uri = Uri.parse("content://sms");
        getContentResolver().registerContentObserver(uri, true, mObserver);
    }

    @Override
    protected void onPause() {
        super.onPause();
        getContentResolver().unregisterContentObserver(mObserver);
    }
}

源碼下載地址:http://download.csdn.net/detail/u012771445/9367900

這是我最近在公司項目中接觸到的Android觀察者模式的一點心得,能力有限,可能存在錯誤和不足的地方,歡迎大家指正,希望我的經驗能夠幫到你。

歡迎大家關注我的博客:http://blog.csdn.net/u012771445

Github:https://gitub.com/shijiacheng


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM