(1)定义
对象间的一对一或者一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都得到通知并被自动更新。
首先需要理解一下观察者设计模式的定义,对象和对象之间的依赖关系是一对一或者一对多,这些对象之间的关系我们称之为被观察者和观察者的关系。
被观察者 :观察者 = 1:N(N包括1)
(2)场景分析
我们想想生活中有哪些一对多的关系:
- 课堂上,老师让同学们朗读一篇课文,学生们听到老师的指令就会一起朗读;
- 教室中,学生们听到眼保健操的广播之后就开始做眼保健操;
- 河里有很多鱼,扔一块石头到鱼群中央,一群鱼被惊吓之后都散开了;
Android中有哪些观察者设计模式?
- 按钮的点击事件;
- 广播(BroadcastReceiver);
- ListView刷新通知;
(3)代码编写顺序
第一步:抽象被观察者
/**
* 抽象被观察者
*/
public interface Observable<T> {
void add(T t);//添加观察者T
void remove(T t);//移除观察者T
void notifyAll(String message);//通知所有的观察者
void notifyItem(int index, String message);//通知某一具体观察者
}
被观察者通常只做三件事:
1.添加观察者
2.删除观察者
3.通知N个观察者(当然也可以只通知指定观察者)
需要声明的是:
- 由于观察者可能有很多,所以具体被观察者需要创建一个集合来存放这些观察者;
- 一次性通知所有的观察者这种方式比较传统,但是在某些需求中并不需要通知所有的观察者;
第二步:具体被观察者
/**
* 具体被观察者(只有一个)
* 我是一个外卖小哥
* 由于被观察者只有一个,所以将被观察者写成单例模式(本例中采用静态内部类单例模式)
*/
public class Deliveryman implements Observable<Observer> {
//一般情况下,由于观察者数量有多个(即被观察者和观察者是一对多的关系),所以这里需要将抽象观察者作为泛型
//个别情况下只需要一对一的关系,可以将具体观察者作为泛型
private List<Observer> observerList = new ArrayList<>();
private Deliveryman(){}
static class SingleHolder{
public static Deliveryman instance = new Deliveryman();
}
public static Deliveryman getInstance(){
return SingleHolder.instance;
}
@Override
public void add(Observer observer) {
observerList.add(observer);
}
@Override
public void remove(Observer observer) {
observerList.remove(observer);
}
@Override
public void notifyAll(String message) {
for (int i=0;i<observerList.size();i++){
observerList.get(i).update(message);
}
}
@Override
public void notifyItem(int index, String message) {
observerList.get(index).update(message);
}
}
被观察者有且只有一个,所以最好用单例设计模式修饰。
这里假设被观察者是一个外卖小哥。
第三步:抽象观察者
/**
* 抽象观察者
*/
public interface Observer {
void update(String message);//更新方法
}
第四步:具体观察者
/**
* 创建具体被观察者
*/
public class PersonalA implements Observer{
private String msg;
@Override
public void update(String message) {
msg = message;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
/**
* 创建具体被观察者
*/
public class PersonalB implements Observer {
private String msg;
@Override
public void update(String message) {
msg = message;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
这里就定义两个观察者,也就是两个订外卖的人,这两个人一直在观望同一个外卖小哥,我的饭到底什么时候到啊?
第五步:怎么使用?
-
让两个人订外卖
PersonalA personalA = new PersonalA(); personalA.setMsg("我是personalA,我在等我的外卖!"); PersonalB personalB = new PersonalB(); personalB.setMsg("我是personalB,我在等我的外卖!"); System.out.println(personalA.getMsg()); System.out.println(personalB.getMsg()); Deliveryman.getInstance().add(personalA); Deliveryman.getInstance().add(personalB);
这时,两个人已经订好外卖,正在等外卖小哥;
-
假如两个人在一个地方,那么将会一起收到外卖
Deliveryman.getInstance().notifyAll("我已将外卖送到"); System.out.println(personalA.getMsg()); System.out.println(personalB.getMsg());
打印日志如下:
图片.png-
假如两个人不在一个地方,那么暂时只有一个人收到外卖
Deliveryman.getInstance().add(personalA); Deliveryman.getInstance().add(personalB); Deliveryman.getInstance().notifyItem(0, "我已将外卖送到");
打印日志如下:
图片.png
以上就是纯Java版的观察者设计模式了。
(4)结合Android分析代码
上面我们说过,Android常用的观察者设计模式有按钮的点击事件、 广播(BroadcastReceiver)、ListView刷新通知。
我们就以按钮点击事件为例。
- 抽象被观察者:无
- 被观察者:View本身
查看源码发现,View中触发onClick事件的方法是:
图片.png- 抽象观察者:
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
- 具体观察者:当前Activity
以上按钮点击事件的讲解我不知道大家能不能理解,那就结合Android再举一个例子
需求:我们要求把数据从一个Activity传递到上一个Activity?
传统的方式如下:
-
从A跳转到B
Intent intent = new Intent(activity, DemoActivity.class); activity.startActivityForResult(intent, requestCode);
-
在B中点击某按钮关闭B,并传递数据
Intent intent = new Intent(); intent.putExtra("aaa", "111222333"); setResult(0x02, intent); finish();
-
在A中的onActivityResult中接收数据
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == 0x01 && resultCode == 0x02 && data != null && data.hasExtra("aaa")){ String text = data.getStringExtra("aaa"); tv_text.setText(text); } }
这种方式看起来比较简单, 但是在大型项目中,requestCode和resultCode传来传去十分影响可读性,并且将所有的逻辑都放在onActivityResult中处理耦合性情况严重,所以不建议这种方式,下面我将带领大家用观察者设计模式实现数据的传递。
假设点击A中的某按钮跳转到B,由B向A传递数据?
我们回忆一下上面说的编写观察者设计模式的步骤:
第一步:编写抽象被观察者
/**
* 抽象被观察者
*/
public interface Observable<T> {
void add(T t);//添加观察者T
void remove(T t);//移除观察者T
void notifyAll(String message);//通知所有的观察者
void notifyItem(int index, String message);//通知某一具体观察者
}
第二步:编写具体被观察者
/**
* 具体被观察者(只有一个)
* 我是一个外卖小哥
* 由于被观察者只有一个,所以将被观察者写成单例模式(本例中采用静态内部类单例模式)
*/
public class Deliveryman implements Observable<Observer> {
//一般情况下,由于观察者数量有多个(即被观察者和观察者是一对多的关系),所以这里需要将抽象观察者作为泛型
//个别情况下只需要一对一的关系,可以将具体观察者作为泛型
private List<Observer> observerList = new ArrayList<>();
private Deliveryman(){}
static class SingleHolder{
public static Deliveryman instance = new Deliveryman();
}
public static Deliveryman getInstance(){
return SingleHolder.instance;
}
@Override
public void add(Observer observer) {
observerList.add(observer);
}
@Override
public void remove(Observer observer) {
observerList.remove(observer);
}
@Override
public void notifyAll(String message) {
for (int i=0;i<observerList.size();i++){
observerList.get(i).update(message);
}
}
@Override
public void notifyItem(int index, String message) {
observerList.get(index).update(message);
}
}
第三步:编写抽象观察者
/**
* 抽象观察者
*/
public interface Observer {
void update(String message);//更新方法
}
第四步:编写具体观察者
这里的观察者就是Activity本身
第五步:如何使用
向被观察者传递观察者对象
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Observer{
private Button bt_observer;
private TextView tv_text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_observer = findViewById(R.id.bt_observer);
bt_observer.setOnClickListener(this);
tv_text = findViewById(R.id.tv_text);
Deliveryman.getInstance().add(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_observer:
//跳转到下级Activity
DemoActivity.start(MainActivity.this, 0x01);
break;
}
}
@Override
public void update(String message) {
//等待下级Activity的通知,设置textview的值
tv_text.setText(message);
}
}
被观察者通知观察者
public class DemoActivity extends AppCompatActivity implements View.OnClickListener{
private Button close;
/**
* 跳转
* @param activity
* @param requestCode
*/
public static void start(Activity activity, int requestCode){
Intent intent = new Intent(activity, DemoActivity.class);
activity.startActivityForResult(intent, requestCode);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demo_main);
close = findViewById(R.id.close);
close.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.close:
Deliveryman.getInstance().notifyAll("发送来新的通知!");
finish();
break;
}
}
}
效果如下:
33.gif这里需要注意的是, 由于被观察者对象Deliveryman和A存在依赖,即使A退出了,也不会被垃圾回收机制回收,这是一个非常严重的内存泄漏, 解决这个内存泄漏的方法如下:
@Override
protected void onDestroy() {
super.onDestroy();
Deliveryman.getInstance().remove(this);
}
当A退出时,主动断开与Deliveryman依赖关系。
网友评论