一:背景
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