美文网首页Android开发Android知识Android技术知识
Rxjava前篇(一):观察者模式之接口回调

Rxjava前篇(一):观察者模式之接口回调

作者: Dynamic_2018 | 来源:发表于2017-11-03 00:38 被阅读91次

    如理解有误或者模棱两可的地方,欢迎指出。共同进步,一起成长!

    前言:为什么要了解观察者模式?

    • 最近准备入手retrofit就遇到了RxJava(由于入坑晚+公司框架太老以至于到现在才开始自学rxJava);RxJava的异步让人佩服,切换线程只需一行代码,序列化事件链式操作......各种Obsever、Observable于是引出了观察者模式。
    • 熟悉接口回调的能很快看懂观察者模式。

    PS:个人认为学以致用,研究茴香豆有几种写法没有意义,但是我们要知其所以然,只会调api就显得很Low了哇。当我们用api出现问题,或者不能满足需求的时候,我们就得往底层看,去看源码,弄懂实现原理。懂了,才敢放心的用各种操作拼凑起来满足自己的需求。


    天线式装逼.jpg

    本篇主要分为:

    • 通用接口回调套路
    • view click回调事件简单分析
    • 接口回调对象的传递(主要是单例模式)

    正文:观察者模式先来看看接口回调

    接口回调异步是怎么实现的?
    接口注册、实现和接口调用分离
    总的来说就分三步走:
    1.定义一个接口(待实现的抽象方法)
    2.实现接口并注册接口
    3.调用接口方法(这里一般是一个类,拥有刚才定义的接口,并对外提供注册接口的方法)然后回调到实现接口的地方执行

    下面以我们常用的点击事件回调看一下:

    • 实现接口并注册接口
    //at mainActivity
       bt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.v("tag","ui主线程发送msg "+"当前线程: "+Thread.currentThread().getId());
                    testThread.mhandler.sendEmptyMessage(0);
                }
            });
    

    跟进去看一下View.OnClickListener()接口,其实从这儿可以看到接口是在View内部的

    • 定义一个接口
    public interface OnClickListener {
            void onClick(View v);
        }
    

    就一个很简单的抽象方法,接口方法默认是public abstract,可以省略

    • 调用接口方法(简单分析下点击事件,不感兴趣的可以直接跳过performclick)
      都是在view.class里面,方便查看省略了很多代码
      可以看到点击事件经过事件分发机制,经过事件分发拦截处理,最后来到view的dispatchTouchEvent(不了解事件分发机制的同学可以自行google,后面有空也会分享一点心得)
    public boolean dispatchTouchEvent(MotionEvent event) {
    //....................
    ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) { 
                    result = true;     //决定是否调用onTouchEvent
                }
    
                if (!result && onTouchEvent(event)) {  //result如果为flase这里调用onTouchEvent
                    result = true;
                }
    }
    //............
    

    可以看到如果上面li.mOnTouchListener.onTouch(this, event)返回为true,result为true; resulet如果为true,往下就不会执行ontouchEvent(进而执行click)了。
    这也是为什么在重写ontouch返回为true,对应view的click事件不会响应。

    onTouchEvent里面会调用performClick。注意是action_up的时候才会调用哦,也就是手指头按下不会,松开才会回调click事件。不知道平时大家有没有注意,不知道的还不赶紧试试= =

    public boolean onTouchEvent(MotionEvent event) {
    //...........
     case MotionEvent.ACTION_UP:
    if (!focusTaken) {                      
                                    if (mPerformClick == null) {
                                        mPerformClick = new PerformClick();
                                    }
                                    if (!post(mPerformClick)) {
                                        performClick(); //====这里调用performClick
                                    }
                                }
    }
    //...........
    

    下面代码可以看到如果mlickListener不为null就可以执行onclick方法,回调到注册接口那里执行。当然对对象判空是一种意识,不然如果忘了注册接口,就不仅仅是不能触发回调,而是直接空指针异常闪退了。

    //at View.class
     public boolean performClick() {
            final boolean result;
            final ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnClickListener != null) {
                playSoundEffect(SoundEffectConstants.CLICK);
                li.mOnClickListener.onClick(this);  //=====这里调用接口的onclick
                result = true;
            } else {
                result = false;
            }
    
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            return result;
        }
    
    就是这么简单.JPG

    虽然看起来代码很多,那是因为android为了实现各种view事件复杂逻辑的代码。我们自己的逻辑如果比较简单,直接获取到subject,然后通过它的接口调用一下方法就行啦。

    贴一个自己写的简单接口回调demo

    //接口CallBackInterface 
    public interface CallBackInterface {
        void onCallBack();
    }
    
    //Subject.class 
    public class Subject {
        private CallBackInterface callBackInterface;
        public void setCallBackInterface(CallBackInterface callBackInterface){
            this.callBackInterface = callBackInterface;
        }
        public void notifyCallBack(){
            if(callBackInterface != null) {
                callBackInterface.onCallBack();
            }
        }
      //一般情况是在这个类调用notifyCallBack();比如突然断网、数据库更新等。如果在其他地方调用,需要获取到subject这个注册了接口的对象。
    }
    //mainActivity
    private Subject subject = new Subject(); //获取subjuect对象是重点
    subject.setCallBackInterface(new CallBackInterface() {
                @Override
                public void onCallBack() {
                    Log.v("tag","at mainActivity  11111");
                }
            });
    
    
    
    //secondeActivity
    private Subject subject; //这个subject要是mainActivity 注册的那个对象,比如用构造函数,set等方法传过来。
    subject.notifyCallBack();
    

    其实我觉得接口回调3步走没什么毛病。主要是subject.setCallBackInerface()和subject.notifyCallBack()的subject要是同一个对象。
    ps:不要觉得没对象就随便new一个........FFFFFF
    如果callBackInterface.onCallBack()在Subject类里面调用,那没什么毛病。如果是在其他地方调用,就要考虑下这个subject注册和调用的地方传递。

    • 上面的demo可以用intent传,不过subject要可序列化,所有Subject要实现Parcelable接口。
    • 普通的类也可以用构造函数传过去。
    • 如果确定Subject对象只有一个(即单例),那么用单例模式是最方便的了。
    • 其他方法(骚操作)


      举个例子.jpg

    Subject换成单例就变成下面这样,注意看注释

    /**
     * Created by Dynamic on 2017/11/2.
     */
    
    public class Subject  {
        private static volatile Subject singleSubject; //volatile是由于jdk1.5之前的漏洞
        private Subject(){ //构造函数私有,确保单例的关键
    
        }
        public static  Subject getInstance(){
            if(singleSubject == null){  //懒汉式,只有需要用到它的时候才去new,不是一开始就new出来
                synchronized (Subject.class){   //线程同步,避免多线程同时执行,new很多个出来(单例)
                    if(singleSubject == null){ 
                        // 双重检查;因为同步块里面第一个null去new,执行完后,已经不为Nulll了
                        //如果singleSubject有了,后面的线程就没必要再去new了
                        singleSubject = new Subject();
                    }
                }
            }
            return singleSubject;
        }
        private CallBackInterface callBackInterface;
        public void setCallBackInterface(CallBackInterface callBackInterface){
            this.callBackInterface = callBackInterface;
        }
        public void notifyCallBack(){
            if(callBackInterface != null) {
                callBackInterface.onCallBack();
            }
        }
    }
    

    上面使用的是双重检测的单例模式了,不管在什么地方哪个类里面,只需要Subject.getInstance,拿到的就一定是同一个subject,非常方便。

    关于单例模式

    • 上面的双重检测方法,在jdk1.5之后才能完全有效。原因是volatile在1.5之前有问题。关于volatile的内存可见性以及指令排序都应该去了解一下。
    • 另外还有一直静态内部类的实现方式。类似于懒汉加载,不过使用静态内部类访问static变量,把同步交给jvm类加载处理,保证线程安全。

    可能有人会问接口回调能干什么呢?

    • 当然是异步啦。像上面的demo,mainActivity注册完接口后,就去干其他事情了,不用等着secondActiviy调用callBack。接口调用的回调通知回来的时候,才去处理回调。
    • 类A(如网络管理类)的事件(wifi断开)回调到类B(数据库管理类)处理(入库断开的事件),就可以访问B的私有成员变量和方法了。(以前不知道接口回调,还是用的handler通信 == )

    下一篇我会以自己的看法来讲述一下观察者模式:以前刚听到这个模式的时候,觉得被观察者好可怜,被围观。后面渐渐发现被观察者才是老大啊,所有信号都是它发的发出来的,然后一群观察者去响应。怎么说呢,就好像是大家(可以规划到一个类集合里面)都在干活。老板突然说了句发工资了,然后大家全部去响应领工资,而且每个员工各自领的工资不一样(每个观察者实现回调不一样)。

    相关文章

      网友评论

        本文标题:Rxjava前篇(一):观察者模式之接口回调

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