編輯:關於Android編程
這篇來講一下觀察者模式,觀察者模式在實際項目中使用的也是非常頻繁的,它最常用的地方是GUI系統、訂閱——發布系統等。因為這個模式的一個重要作用就是解耦,使得它們之間的依賴性更小,甚至做到毫無依賴。以GUI系統來說,應用的UI具有易變性,尤其是前期隨著業務的改變或者產品的需求修改,應用界面也經常性變化,但是業務邏輯基本變化不大,此時,GUI系統需要一套機制來應對這種情況,使得UI層與具體的業務邏輯解耦,觀察者模式此時就派上用場了。
PS:對技術感興趣的同鞋加群544645972一起交流。
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象狀態發生改變時,它的所有依賴者都會收到通知並自動更新。它實現了 Subject 和 Observer 之間的松耦合,Subject只知道觀察者實現了 Observer 接口,主題不需要知道具體的類是誰、做了些什麼或其他任何細節。任何時候我們都可以增加新的觀察者。因為主題唯一依賴的東西是一個實現Observer 接口的對象列表,所以我們可以隨時增加觀察者,同樣的,也可以在任何時候刪除觀察者,當然更不用去關心觀察者的類型,只要實現了Observer接口即可,Subject 最後只會發通知給實現了 Observer 接口的觀察者。Subject 和 Observer 之間實現松耦合之後,雙方代碼的修改都不會影響到另外一方,當然前提是雙方得遵守接口的規范(接口隔離原則)。
觀察者模式使用的場景:
vcfJq7m5s8k8L3A+DQpTdWJqZWN0o7qz6c/z1vfM4qOs0rK+zcrHsbu527LstcS9x8mro6yz6c/z1vfM4rDRy/nT0LnbsuzV37bUz/O1xNL908Oxo7Tm1NrSu7j2vK+6z8Dvo6zDv7j21vfM4ra8v8nS1NPQyM7S4sr9wb+1xLnbsuzV36Oss+nP89b3zOLM4bmp0ru49r3Tv9qjrL/J0tTU9rzTus3JvrP9uduy7NXfttTP86GjQ29uY3JldGVTdWJqZWN0o7q+38zl1vfM4qOsuMO9x8mrvavT0LnY17TMrLTmyOu+38zluduy7NXfttTP86Os1Nq+38zl1vfM4rXExNqyv9e0zKy3osn6uMSx5Mqxo6y4+Mv509DXorLhuf21xLnbsuzV37eis/bNqNaqo6y+38zl1vfM4r3Hyau21M/z09a90Nf2vt/M5bG7uduy7NXfvcfJq6GjT2JzZXJ2ZXKjurPpz/O527Ls1d+jrLjDvcfJq8rHuduy7NXftcSz6c/zwOCjrMv8tqjS5cHL0ru49rj80MK907/ao6zKubXD1Nq1w7W91vfM4rXEuPy4xM2o1qrKsbj80MLX1Ly6oaNDb25jcmV0ZU9ic2VydmVyo7q+38zltcS527Ls1d+jrLjDttTP88q1z9az6c/zuduy7NXfvcfJq8v5tqjS5bXEuPzQwr3Tv9qjrNLUsePU2tb3zOK1xNe0zKyx5LuvyrG4/NDC19TJ7bXE17TMrKGjoaGhodPQvLi149Do0qrXotLitcTKx1N1YmplY3Qgus0gT2JzZXJ2ZXIgysfSu7j20ru21LbgtcS52M+1o6zSsr7NysfLtbnbsuzV39a70qrKtc/WIE9ic2VydmVyIL3Tv9qyorDR19S8uteisuG1vSBTdWJqZWN0INbQvs3E3Lm7vdPK1bW9z/vPosrCvP6ju0phdmEgQVBJ09DE2tbDtcS527Ls1d/Eo8q9wOCjumphdmEudXRpbC5PYnNlcnZhYmxlIMDgus0gamF2YS51dGlsLk9ic2VydmVyIL3Tv9qjrNXit9ax8LbU06bXxSBTdWJqZWN0ILrNIE9ic2VydmVyILXEvcfJq6O7yrnTwyBKYXZhIEFQSSC1xLnbsuzV38Sjyr3A4KOs0OjSqtei0uK1xMrHsbu527Ls1d/U2rX308Mgbm90aWZ5T2JzZXJ2ZXJzKCkguq/K/c2o1qq527Ls1d/Wrsew0ru2qNKqtffTwyBzZXRDaGFuZ2VkKCkguq/K/aOs0qqyu8i7uduy7NXfzt63qL3Ttb3NqNaqo7vKudPDIEphdmEgQVBJILXEyLG149KyutzD98/Uo6zTydPaIE9ic2VydmFibGUgysfSu7j2wOCjrGphdmEg1rvUytDttaW8zLPQtcTIsbXjvs21vNbCxOPI57n7zazKsc/r0qq78cihwe3Su7j2uLjA4LXEyvTQ1Mqxo6zE49a7xNzRodTxysrF5Mb3xKPKvbvy1d/Kx8Tasr/A4LXEt73KvaOstvjH0tPJ09ogc2V0Q2hhbmdlZCgpILqvyv3OqiBwcm90ZWN0ZWQgyvTQ1KOsy/nS1MTjs/23x7zMs9AgT2JzZXJ2YWJsZSDA4KOst/HU8sTjuPmxvs7et6jKudPDuMPA4LXEyvTQ1KOs1eLSss6lsbPBy8novMbEo8q9tcTUrdTyo7q24NPD1+m6z6OsydnTw7zMs9Chow0KPHA+Jm5ic3A7PC9wPg0KPGgyIGlkPQ=="示例與源碼">示例與源碼
觀察者模式寫法有很多種,只要遵循將被觀察者和觀察者解耦思想的方法都是可以的,列舉三種我在開發中常見的方法,以 firstmoduel 作為 secondmodule1 和 secondmodule2 的父 module ,最後 application 以 secondmoduel1 和secondmodule2 作為父 module,形成一個菱形的關系為例:
使用Java API的寫法其實就是使用 java.util.Observable 類和 java.util.Observer 接口,根據 uml 類圖構造觀察者,我們簡單實現一個在 secondmodule2 中通知 secondmodule1有數據源的改變,並且讓 secondmodule1 打印出對應日志的功能。因為 secondmodule1 和 secondmodule2 是一個同級關系,所以無法相互調用,只能通過 firstmodule 作為主題使用觀察者模式進行通信。
Subject 在使用 Java API 時其實就是 Observable 類,所以我們要在 firstmodule 中繼承該類實現一個 ConcreteSubject 用來保存所有的觀察者,接著在 secondmodule1 中實現 Observer 接口來生成一個觀察者,最後在secondmodule2 中通知 firstmodule 數據源改變,最後通過 firstmodule 通知到 secondmodule1 ,代碼如下:
firstmoduel/DataObservable.class 中利用單例模式進行觀察者的管理和通知:
public class DataObservable extends Observable{
private static volatile DataObservable instance;
private DataObservable() {}
public static DataObservable getInstance() {
if (instance == null){
synchronized (DataObservable.class) {
if (instance == null){
instance = new DataObservable();
}
}
}
return instance;
}
public void notifyDataChanged(DataBean data) {
setChanged();
notifyObservers(data);
}
}
firstmoduel/DataBean.class 數據類:
public class DataBean {
public int temperature;
}
secondmodule1/DataObserver.class 中判斷如果為正確的 Observerable 則打印日志:
public class DataObserver implements Observer {
private static final String TAG = "DataObserver";
@Override
public void update(Observable observable, Object data) {
if (observable instanceof DataObservable){
if (data instanceof DataBean){
Log.e(TAG, ((DataBean)data).temperature+"");
}
}
}
}
secondmodule2/DataNotify.class
public class DataNotify {
public static void notifyDataChanged(){
DataBean bean = new DataBean();
bean.temperature = (int) (Math.random() * 40);
DataObservable.getInstance().notifyDataChanged(bean);
}
}
最後在 MainActivity 中調用 DataNotify 類的相應方法通知 DataObserver 溫度數據變更即可:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
DataObservable.getInstance().addObserver(new DataObserver());
}
@Override
protected void onDestroy() {
super.onDestroy();
DataObservable.getInstance().deleteObserver(new DataObserver());
}
...
public void onClick(View v) {
if (v.getId() == R.id.btn_data_change_1){
DataNotify.notifyDataChanged();
}
...
}
最後能夠成功打印出正確日志,也就是實現了 android 中兩個同級 module 之間的通信。
當然如果覺得麻煩不使用 Java API 的這兩個類,自己去實現的話也是完全可以的,而且有時可操縱性會比 Java API 更好一些。
有時候我們不一定全部要實現自同一個 Observer 接口,根據實際情況我們可以在完全解耦的情況下將多個 Observer 注冊到 Subject 中,並且根據情況只通知到我們想要通知的一個 Observer 中,聽起來很亂,其實看一張 uml 圖就清楚了:
這種方式其實最重要的就是 Subject 這個角色了,他承擔了大部分的工作量,我們先實現若干個 Observer 的角色:
firstmodule/IDataListenerOne.class
public interface IDataListenerOne {
void OnDataChanged(DataBean data);
}
firstmodule/IDataListenerTwo.class
public interface IDataListenerTwo {
void OnDataChanged(DataBean data);
}
非常簡單的幾個接口,用來在 secondmodule1 和 secondmodule2 之間進行通信。接著是最重要的 Subject 和 ConcreteSubject 角色:
firstmodule/IMultiDataObservable.class
public interface IMultiDataObservable {
/**
* 增加觀察者
*/
void addObserver(Object observer);
/**
* 刪除觀察者
*/
void deleteObserver(Object observer) throws IllegalArgumentException;
/**
* 查找觀察者
*/
ArrayList findObserver(Class clazz);
}
firstmodule/MultiDataObservable.class
public class MultiDataObservable implements IMultiDataObservable {
private static volatile MultiDataObservable instance;
private ArrayList
這個類主要是有三個方法,增刪找,特別是 findObserver 方法,它通過傳入的 Class 對象從觀察者 list 中找到符合要求的 Observer 並且返回,需要特別留意的一點是需要使用的方法是 Class.isInstance(Object object) 方法,而不是直接判斷 object.getclass() == clazz ,因為後者的這種判斷如果使用的是匿名內部類方式,他的類名會是 DataCommunicate$1 這種樣式,所以這種方法是不可行的。最後在 secondmodule1 和 secondmodule2 中只要根據使用調用相應的方法即可,以secondmodule2 為例:
secondmodule2/DataCommunicate.class
public class DataCommunicate {
private static final String TAG = "DataCommunicate";
private static IDataListenerTwo listenerTwo = null;
public static void registerDataListenerTwo() {
IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
listenerTwo = new IDataListenerTwo() {
@Override
public void OnDataChanged(DataBean data) {
Log.e(TAG, data.temperature + "");
}
};
dataObservable.addObserver(listenerTwo);
}
public static void notifyDataListenerOne() {
IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
ArrayList lists = dataObservable.findObserver(IDataListenerOne.class);
DataBean bean = new DataBean();
bean.temperature = (int) (Math.random() * 40);
for (IDataListenerOne listener : lists) {
listener.OnDataChanged(bean);
}
}
public static void unRegisterDataListenerTwo() {
IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
dataObservable.deleteObserver(listenerTwo);
}
}
最後也可以成功通信,這種方式的優點是完全可以自定義 Observer 這個接口,接口中的方法可以任意定義,具有很大的自由性,便於統一管理所有的觀察者,非常方便。
事件總線大家想必已經看見過很多了,它也是一個典型的觀察者模式用例,列舉一下我常見的3個框架:
greenrobot/EventBusAndroidEventBus">AndroidEnventBusOtto它們原理也不復雜, 對比之類的網上資料很多,比如Otto 框架的效率大多數時候會比不上 EventBus 等,感興趣想要具體了解的自己動動手收集一下,我這裡以使用最廣泛的 greenrobot/EventBus 為例,來實現 secondmodule1 和 secondmodule2 之間的通信:
public class EventNotifier {
private static volatile EventNotifier instance;
private EventNotifier() {
}
public static EventNotifier getInstance() {
if (instance == null) {
synchronized (EventNotifier.class) {
if (instance == null) {
instance = new EventNotifier();
}
}
}
return instance;
}
public void sendEvent() {
DataBean bean = new DataBean();
bean.temperature = (int) (Math.random() * 40);
EventBus.getDefault().post(bean);
}
}
secondmodule2/EventObserver.class
public class EventObserver {
private static final String TAG = "EventObserver";
private static volatile EventObserver instance;
private EventObserver() {
}
public static EventObserver getInstance() {
if (instance == null) {
synchronized (EventObserver.class) {
if (instance == null) {
instance = new EventObserver();
}
}
}
return instance;
}
public void registerObserver() {
EventBus.getDefault().register(this);
}
public void unRegisterObserver() {
EventBus.getDefault().unregister(this);
}
@Subscribe
public void onEventMainThread(DataBean bean) {
Log.e(TAG, bean.temperature + "");
}
}
特別需要注意的是,onEventMainThread 函數需要加上 @Subscribe 注解,要不然是無法工作。最後在 MainActivity 中調用相關函數即可:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
EventObserver.getInstance().registerObserver();
}
@Override
protected void onDestroy() {
...
EventObserver.getInstance().unRegisterObserver();
}
@Override
public void onClick(View v) {
...
else if (v.getId() == R.id.btn_data_change_3) {
EventNotifier.getInstance().sendEvent();
}
}
最後當然也是能夠成功!
觀察者模式在實際開發過程中使用的場景真的挺多,比如 Activity 中兩個 fragment 之間的通信,上面描述的兩個同級 module 之間的通信,上層 module 與下層 module 的通信,兩個 Activity 之間維護一個數據源需要做到數據實時同步等等場景,使用觀察者模式之後思路簡單清晰,可維護性好,而且最重要當然是耦合性低利於擴展,Android 官方源碼中的 Listview 和 Adapter 也是使用了觀察者模式的典型例子。
觀察者模式的優點和缺點總結一下,優點:
https://github.com/zhaozepeng/Design-Patterns/tree/master/ObserverPattern
如何用ES文件浏覽器查看wifi密碼。ES文件浏覽器是一個能管理手機本地、局域網共享、FTP和藍牙文件的管理器。其實就是一個電腦和手機網絡文件傳輸軟件。可大
前言給自己的APP增加相機是一個不錯的功能,在我們打開相機時,如果能動態給我們的臉上貼上標簽,或者是貼上一個卡通的眼睛,最後點擊拍照,一張合成的圖片就產生了,這是不是一個
上一篇面試總結,主要講了Activity 和Intent ,這篇主要講剩下的四大組件,面試中基礎較多的就是四大組件,需要了解的知識點也很多。上一篇文章 &nb
本文是在完成了Android百度地圖應用開發基礎知識的基礎上繼續實現的。本文實例為大家分享了Android如何顯示地圖,並為後續內容做准備,供大家參考,具體內容如下&nb