美文网首页设计模式
Android之观察者设计模式

Android之观察者设计模式

作者: NoBugException | 来源:发表于2019-02-22 02:04 被阅读55次

(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依赖关系。

相关文章

网友评论

    本文标题:Android之观察者设计模式

    本文链接:https://www.haomeiwen.com/subject/stwjyqtx.html