java回调

来源:互联网 发布:易建联nba生涯数据 编辑:程序博客网 时间:2024/06/07 05:41

一:背景

why-为什么需要回调关于什么是回调网上很多,但是很少有从为什么需要回调开始,所以下面从基本调用开始到最后为什么使用回调
1. 同步调用

A类中a()去调用B类的b()。
一个最大的问题,阻塞,在a()方法执行了2行代码在去调用b()方法,但是b()执行了耗时的操作,那么a()只能等到b()执行完,在能去执行或许的代码,产生了整个流程的阻塞。
所以这个只适合非耗时操作,也是项目中用到最多的方法。
2.异步调用

很多时候A类中a()去调用B类的b()需要耗时操作,就必须异步调用了
a()中开启一个新的线程执行b()这样,无论b()操作多长时间,都不会影响a()的正常执行,有利就有弊,这里如果b()是执行一些下载,刷新和a()没交互的方法不会有任何问题,但是如果a()需要b()提供一些参数,那么就不可以实现了。这里就需要回调了

二:回调

what-什么是回调

上图:A类中a()调用了b(),在b()执行完操作后又去调用了A类中的callback()通知最后的结果。
回调的理解:简单的说,A类中a()中调用了B类中b(),在一段时候后b()执行完操作,然后把自己结果作为一个或多个参数传递个A使用的过程。

三:写回调

how-如何写回调
1.基本版本
情况是这样的,学生小明要做一个加法,但是太笨了,不会!
就找到了小红,小红非常聪明,俗称超级计算机。
小明说“小红你帮我计算下1+1等于几,然后直接帮我写到作业本上,拜拜!”
然后小明跑了,小红在2s后做完,写出了最后结果。
ok
这就是最基本的回调。
小明(Student )告诉小红 (SuperCalculator )2个数字
小红 (SuperCalculator )计算完成后,在小明的本子上写出答案 (调用Student 的 fillBlank())

>public class Student {    private String name = null;    public Student(String name) {        this.name = name;    }    public void callHelp(int a, int b) {        new SuperCalculator().add(a, b, this);    }    public void fillBlank(int a, int b, int result) {        System.out.println(name + "求助小红计算:" + a + " + " + b + " = " + result);    }}>public class SuperCalculator {    //只能接收单一学生类,没有扩展性    public void add(int a, int b, Student xiaoming) {        int result = a + b;        xiaoming.fillBlank(a, b, result);    }}>public class Test {scxda    public statica void main(String[] args) {        int a = 11;        int b = 12;        Student s = new Student("小明");        s.callHelp(a, b);    }}

具体代码如上面,很简单,没毛病。但是,没有扩展性,小红现在只能为小明(Student)服务,如果老师来找小红,帮忙计算一下2+2等于几就不可以了,因为小红,只能接收单一的Student类,不接受Teacher。如何办
2.升级版本
增加一个父类,doJob,Student Teacher 都继承了doJob这个父类,这样小红可以愉快的算加法了,无论是谁只要是doJob的子类就可以找小红帮忙了。但是这是最后结果吗?不是的!

public abstract class doJob{    public abstract void fillBlank(int a, int b, int result);}public class Teacher extends doJob {    private String name = null;    public Teacher(String name) {        this.name = name;    }    public void callHelp(int a, int b) {        new SuperCalculator().add(a, b, this);    }    public void fillBlank(int a, int b, int result) {        System.out.println(name + "要求小红计算:" + a + " + " + b + " = " + result);    }}public class Student extends doJob {    private String name = null;    public Student(String name) {        this.name = name;    }    public void callHelp(int a, int b) {        new SuperCalculator().add(a, b, this);    }    public void fillBlank(int a, int b, int result) {        System.out.println(name + "求助小红计算:" + a + " + " + b + " = " + result);    }}public class SuperCalculator {    //优扩展性,所有实现接口的类都可以传递进入    public void add(int a, int b, doJob d) {        int result = a + b;        d.fillBlank(a, b, result);    }}public class Test {    public static void main(String[] args) {        int a = 11;        int b = 12;        int c = 100;        Student s = new Student("小明");        Teacher t = new Teacher("Teacher");        s.callHelp(a, b);        t.callHelp(a,c);    }}

3.在次升级版本
几乎一样,但是有一个核心的区别接口
没错,这才是最常见的回调版本。
小明,和老师没有同一个父类,他们只有一个共同的接口 doJob
why?
接口和父类实现的效果一样,但是有几个很大的区别
1.降低耦合性,为了一个简单的加法功能,就去继承一个父类,太浪费了,java是单继承的,只能有一个父亲,为了一个功能就去叫爸爸,他浪费太奢侈了。父类和子类的亲密度太高了,这里没有这个需求(一般多个子类有一些公共的方法参数才会需要去继承同一个父类的)
2.减少暴露的数据,如果使用了一个父类,那么小红在计算的时候不仅仅回调fillBlank一个方法,还可以知道很多别的方法,例如小明的零食在哪了,老师喜欢那个小朋友了
3.为了后续的扩展:如果现在增加需求算乘法2*2,但是小红不会怎么办,都继承了一个父类,那只能去修改这个父类,新增一个方法,然后找到小王,让他帮忙计算,瞬间复杂了很多,很多没有关系的人在了一起。而且每增加一次都要修改一次,太麻烦了。
所以,这里最简单的就是接口了:说白了,接口能做的父类都能做,但是没有必要,这里只是需要一个契约!
接口就是这个契约
小红只需要吧计算好的结果给fillBlank()就可以了
小明,老师也只是需要知道fillBlank()给了什么参数就可以,别的情况,双方都不需要,也没有必要知道!

public interface doJob{     void fillBlank(int a, int b, int result);}public class Student implements doJob {    private String name = null;    public Student(String name) {        this.name = name;    }    public void callHelp(int a, int b) {        new SuperCalculator().add(a, b, this);    }    public void fillBlank(int a, int b, int result) {        System.out.println(name + "求助小红计算:" + a + " + " + b + " = " + result);    }}public class Teacher implements doJob {    private String name = null;    public Teacher(String name) {        this.name = name;    }    public void callHelp(int a, int b) {        new SuperCalculator().add(a, b, this);    }    public void fillBlank(int a, int b, int result) {        System.out.println(name + "要求小红计算:" + a + " + " + b + " = " + result);    }}public class SuperCalculator {    //优扩展性,所有实现接口的类都可以传递进入    public void add(int a, int b, doJob d) {        int result = a + b;        d.fillBlank(a, b, result);    }}public class Test {    public static void main(String[] args) {        int a = 11;        int b = 12;        int c = 100;        Student s = new Student("小明");        Teacher t = new Teacher("Teacher");        s.callHelp(a, b);        t.callHelp(a,c);    }}

四:Android中的应用

回调应用非常多,android中最常见的一个应用就是各种点击事件,只要有交互的界面,都回用到View的回调。
核心都是一样onClick
1系统view内部增加一个OnClickListener接口包含一个 onClick方法。
2 定义回调接口的成员变量
public OnClickListener mOnClickListener;
3view中增加一个setOnClickListener() 设置回调接口对象成员变量
4外部的activity需要实现点击效果的控件,去调用setOnClickListener方法(所有控件最终的父类View,所以全部控件都是具有setOnClickListener方法),需要一个参数View中定义接口的实现,为了方便一般都是使用匿名内部类了。
5最后执行,在用户点击一个控件后,最终会执行performClick方法,执行mOnClickListener的onClick方法。
android中接口的好处,假设有2个button,1号点击后去刷新界面,2号点击后去下载内容。这些系统是不知道的,系统只会通知什么时候点击了,点击后的操作就是具体程序员完成了,各司其职,降低耦合度。
这个流程完成,下面是简化后的源码,看明白后对接口的理解可以更上一层了。

public class ViewT {    /*     * 定义回调接口的成员变量     */    public OnClickListener mOnClickListener;    /**     * 声明回调接口     * 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(ViewT v);    }    /**     * 设置回调接口对象成员变量     * Register a callback to be invoked when this view is clicked. If this view is not     * clickable, it becomes clickable.     *     * @param l The callback that will run     */    public void setOnClickListener(OnClickListener l) {        mOnClickListener = l;    }    /**     * 调用回调接口对象中的方法     * Call this view's OnClickListener, if it is defined.  Performs all normal     * actions associated with clicking: reporting accessibility event, playing     * a sound, etc.     *     * @return True there was an assigned OnClickListener that was called, false     * otherwise is returned.     */    public boolean performClick() {        mOnClickListener.onClick(this);        return true;    }} //这里就是我们一般写的方法了,写一个匿名内部类实现接口,在系统调用onClick方法时候接受到消息在处理了       ttview.setOnClickListener(new ViewT.OnClickListener() {           @Override           public void onClick(ViewT v) {               //接受到 模拟的ViewT对象,可以做处理了           }       });

五:总结

这就是回调从无到有到强大的过程,这里只是写了一部分,太多例子文章都是最后一步。
很长时间都想不明白为什么要这样,最近收集不少文章整理后发现,要从根上面理解一个概念才能更好的学习理解了。
参考文章:
http://www.importnew.com/19301.html
http://www.cnblogs.com/xrq730/p/6424471.html
http://www.jianshu.com/p/3f86b7949f20

原创粉丝点击