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

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

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

  • 最近准备入手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通信 == )

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 订阅报纸的过程## 来考虑实际生活中订阅报纸的过程,这里简单总结了一下,订阅报纸的基本流程...
    七寸知架构阅读 4,379评论 5 57
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,295评论 18 399
  • 2016这一年,在甜蜜中开始在寂静中收尾。 从恋情的甜蜜和异地的思念中开始的新年,格外美好。可是慢慢嘴脸并不如想...
    f3593832bbd9阅读 187评论 0 0