在看回调之前,我们必须明白什么是同步调用与异步调用。这两个概念其实很好理解,同步与异步是相对时间来说的:
- 同步调用:同一时间只能做一件事
- 异步调用:同一时间可以做多件事,一般通过以下两种手段实现:
- 多线程:为需要阻塞的方法单独启动一个线程执行
- 回调:执行完成后通过回调,通知主线程执行完毕或者获取执行结果
多线程大家一看都能明白,那回调到底是什么呢?
1.回调机制
回调是实现异步调用的一种手段,是为了异步通信服务的,可以细分为以下几种:
1.1 基本回调
基本形式如下:
A对象 ----B.fb(A) -----> B对象 // 调用B的fb()时传入A本身(this)
A对象 <-----A.fa()------ B对象 // B就可以直接调用A,fa()就是回调方法
1.2 接口回调
基于接口回调:若只能给fb()传入A类型那么就是强耦合,所以一般给 fb() 传入的是一个接口,A实现这个接口
AImpl ----B.fb(IA) -----> B对象 // IA是抽象接口,AImpl是IA的实现类
AImpl <-----IA.fa()------ B对象
1.3 异步回调
异步回调就是给B.fb()方法单独起一个线程去执行
- 使用场景:当A要调用B的某个方法,且该方法会产生阻塞,或耗时较长,则可以将该方法做成异步调用
- 异步通信:通过回调机制进行通信,执行完后告知A
AImpl ----B.fb(IA) -----> B对象 // B.fb(IA)单独起一个线程执行
AImpl <-----IA.fa()------ B对象
2.代码示例
概念说完了,下面就上代码…
2.1 同步调用
码农(Programmer)现在需要做几件事情:
- 敲代码一整天(敲代码)
- 将今天工作代码上传到Github上(上传代码)
- 备注一下完成日期(记录下完成的日期)
下面我们用程序去实现:程序一共就三个类:
- Programmer(码农类)
- Github(Github类 用于保存码农提交的代码)
- TestDemo(用于测试并显示结果)
如下所示:
public class Programmer {
public void code(){
System.out.println("coding"); //step1.写代码
new Github().push(); //step2.提交代码
recordToday(); //step3. 记录的日期
}
public void recordToday(){
System.out.println("i'm done " + new Date());
}
}
// 只负责上传代码
public class Github {
public void push(){
System.out.println("git push");
}
}
// 让Programmer工作
public class TestDemo {
public static void main(String args[]){
Programmer p = new Programmer();
p.code();
}
}
执行结果如下:
2.2 基本回调
可否给苦逼程序员一条生路,让他提交代码之后就下班,像记录日期:recodeToday()这种事让Github类去负责。
换句话说,Github类的push()函数执行完后,Github类去调用Programmer类的recodeToday()方法,而不是Programmer自己去调用,略微改动后
// A
public class Programmer {
public void code(){
System.out.println("coding");
new Github().push(this); // B.fb(this)
// 这里没有 step3,码农没有自己调用 recordToday()方法.
}
// A.fa(),回调方法
public void recordToday(){
System.out.println("i'm done " + new Date());
}
}
// B
public class Github {
public void push(Programmer p ){ // push的参数多了Programmer类
System.out.println("git push");
p.recordToday(); // 执行回调
}
}
最后执行结果不变
2.3 接口回调
如果现在不只有Programmer了还有Teacher和Student,那GitHub的push方法难道要再重载?最好的办法是push依赖接口
// IA,回调接口
public interface RecodeInterface {
public void recode();
}
// B
public class Github {
public void push(RecodeInterface r ){ // 依赖接口
System.out.println("git push");
r.recode(); // A.fa(),执行回调
}
}
// AImpl,实现了接口
public class Programmer implements RecodeInterface{
public void code(){
System.out.println("coding");
new Github().push(this); // B.fb(A)
}
@Override
public void recode() { // 回调函数
System.out.println("Programmer done " + new Date());
}
}
public class Student {
public void code(){
System.out.println("coding");
new Github().push(new StudentRecodeToday());
}
// AImpl,也可以单独抽象出一个内部类
public class StudentRecodeToday implements RecodeInterface{
@Override
public void recode() {
System.out.println("Student done "+ new Date());
}
}
}
2.4 异步回调
// IA,回调接口
public interface mycallback {
void onData(Object message); // 正常处理
void onError(Exception e); // 异常处理
}
// AImpl,实现了回到接口
public class Client implements mycallback{
int count=0;
@Override
public void onData(Object message) {
count++;
System.out.println("received message:"+message.toString());
}
@Override
public void onError(Exception e) {
System.out.println("error!");
}
public void send(String message){
// 异步回调核心!!! 为B.fb(IA)单独启动一个线程
Thread thread=new Thread(new Server(Client.this,message));
thread.start();
}
}
// B
public class Server implements Runnable{
mycallback c;
Object message;
public Server(mycallback cl,Object o){
c=cl;
message=o;
}
@Override
public void run() {
try {
c.onData(message+" after server"); // 执行回调
}catch (Exception e){
c.onError(e);
}
}
}
启动类
public class Work{
public static void main(String[] args){
Client c1=new Client();
c1.send(hello);
System.out.println("do others...");
}
}
结果
do others...
received message:hello