美文网首页设计模式
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