您的位置:首页 > 编程语言 > Java开发

Java进阶——回调机制详解及实例

2016-08-02 22:51 651 查看

引言

工作关系一直都是和Android源码、系统app打交道,发现回调机制被大量使用,觉得有必要把Java回调机制的理解归纳总结一下,以便能在工作中对于源码的理解更深彻些,同时也是整理形成完整的体系,于是翻阅了”Java圣经”结合自己的理解和网上道友的一些见解,再次总结下神奇的回调。

一、回调概述

Java中的回调(callback)是很重要的一个机制。项目模块之间总是存在着一定的接口,从调用方式上看可分为三类:同步调用回调和异步调用。其中同步调用是一种阻塞式调用,我们在开发中经常使用的;回调是一种双向的调用模式(即 被调用方的接口被调用时 也会调用对方的接口,“If you call me, i will call back”。 ),而异步调用是一种类似消息或事件的机制,解决了同步阻塞的问题比如说A通知B后,他们各走各的路,互不影响,不用像同步调用那样,A通知B后,非得等到B走完后,A才继续走。所以说回调是异步的基础。通俗点说就是——A类中通过B类的实例对象调用B类中的某个方法fun,然后B类通过回调接口反过来调用A类中的方法callback(因为A必须实现回调接口,这样回调接口的方法就成为了A类的方法),而callback这个方法就叫A的回调方法

二、经典的回调方式

定义一个回调接口类ICallback——这里是为了定义出A想要委托出去给B做的事,可能A还未决定好具体要做什么,又或许不只A想要委托别人做这些事,其他类D、F也想委托同样的事,抑或同名不同的事,为了便于扩展,设计为接口,将来无论是谁,想要委托出去只需要去实现这个接口即可,其实这方面是封装和设计的基本思想,面向对象方面的就不扯太远了。

class A实现接口ICallback ——此时A已经决定了具体要做什么了,于是去实现回调接口,即这个接口里的方法就是A的回调方法

class A中包含一个class B的引用b ——我们知道Java完全是面向对象的,如果想要调用一个类的非静态方法,首先得得到一个类的实例然后才能去调用其非私有方法,所以手段之一就是把目标类变为自己的成员

class B实现一个参数包含回调接口ICallback的非私有方法process (ICallback callback) —— A委托B,所以B必须给A提供一个提出委托的请求的渠道,同时在B处理了之后告知给A,所以B类的这个私有方法process就是为了给A提出委托和回馈结果的,而B类则是通过回调接口反 馈结果的

class A实现提出委托的方法delegate,在在这个方法中通过自己的成员变量B调用B的方法process 并把A的实例作为实参传递过去process (a)完成回调的注册 ——要想委托别人办事,你肯定得主动提出请求,而B已经公开了相关的渠道,那么A必须去调用提供B的渠道告知B,所以A必须得把自己的实例作为实参传递过去,因为B是这么定义的,即you call me

class B在自己的process 方法中调用ICallback的方法 ——B如果处理完毕之后就应该把结果尽早告诉给A,所以只能通过A提供的渠道,而B并没有A的对象,只能通过ICallback的实例来调用A的方法告知,这也是为什么要定义ICallback为实参的原因之一,即I’ll call you back。

三、回调机制的实例

1、异步回调

就以当前一个时政作为例子吧,两位跳梁小丑——霉国和菲猴子,比如说霉国老早就想要去干扰CN的发展,于是想出一计围剿CN,但又不想当众撕破脸公开交涉(毕竟国际舆论还是有点威严的),然后威逼利诱下菲猴子欣然接单,三年前霉国通知其爪牙,可以开始执行计划了,但是菲猴子本身也需要发展,一时之间还未有结果,直到今年才算是完成了任务并及时通知了霉国,霉国接到通知后马上派大船来装逼。不知道是否读得懂?ORZ……

1.1、定义一个回调接口类即定义class A的回调方法

package carzymo.callback;
/**
* @author cmo
* 一个回调接口,相当于连接A和B的桥梁,A实现了这个接口,则接口里的方法就是A的回调方法,B则可以调用这个方法来实现回调A
*/
public interface IHarass {
public void harass(String report);//此例中围剿CN就是被封装成这个方法
}


1.2、实现class A

此例中很明显霉国是class A,我们再来明确下他的角色和功能,一共有三种:明确委托B做的具体的事、处理自己的其他事、向B发出委托或者命令、接收确认B完成任务结果。

//class A实现了接口ICallback
public class DamnUSA implements IHarass {

private JokeyMonkey monkey;//class A中包含一个class B的引用b
public DamnUSA(JokeyMonkey monkey){
this.monkey=monkey;
}

//通过这个方法真正触发回调,即向B发出委托
protected void sendCommond(final String commond){
//开启一个线程,异步调用
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("DamnUSA调用JokeyMonkey的方法executeHosterCommond发布命令\n");
monkey.executeHosterCommond(DamnUSA.this, commond); //A调用B的方法,又名为注册回调

}
}).start();
handleInsideIssue();//发布完命令,就处理自己的事中...
}
/*
*DamnUSA的回调方法,定义它的目的就是给JokeyMonkey在完成任务后能及时通知给DamnUSA
*/
@Override
public void harass(String report) {
System.out.println("JokeyMonkey调用harass来告知DamnUSA任务已完成"+report+"\n");
}
private void handleInsideIssue(){
System.out.println("DamnUSA专心处理自己的其他事中——转移国内矛盾中...\n");
}
}


1.3、实现B

package carzymo.callback;

public class JokeyMonkey {

//class B实现一个参数包含回调接口ICallback的非私有方法process (ICallback callback)
public void executeHosterCommond(IHarass damnUSA, String commond) {
System.out.println("JokeyMonkey接收到DamnUSA的命令————>"+commond+"\n");
//模拟处理自己的其他事
for(int i=0;i<1000000;i++){
if(i==0){
System.out.println("...Monkey处理自己的事中...\n");
}
}
String report="DamnUSA主子分配的事情已完毕——————>报告主子,任务已完成,请明示。";//任务完成之后
damnUSA.harass(report);//B调用A,即调用A的回调方法告知A
}
}


1.4、开启回调

package carzymo.callback;

public class JokeyShow {
public static void main(String[] args) {
JokeyMonkey monkey=new JokeyMonkey();
DamnUSA damn=new DamnUSA(monkey);
String report="开始实施围剿CN计划,你丫的快去准备";
damn.sendCommond(report);//回调开始的起点
}
}


运行结果:



2、匿名回调机制

还是上面的例子,不同的是我们把A用匿名类来代替。

public static void main(String[] args) {
JokeyMonkey monkey = new JokeyMonkey();
monkey.executeHosterCommond(new IHarass() {

@Override
public void harass(String report) {
System.out.println("JokeyMonkey调用harass来告知DamnUSA任务已完成"
+ report + "\n");
}
}, "开始实施围剿CN计划,你丫的快去准备");
}




小结

其实回调的本质就是:双向调用,A调用了B的方法,B的方法开始执行,B执行完了,再调用A的方法。再结合我们面向接口的编程的调用关系,一般在三层中,当业务层调用数据层时,是不需要把业务层自身传递到数据层的,并且这是一种上层调用下层的关系,再比如我们在用各种框架的时候,一般直接调用框架提供的API就可以了,但回调不同,当框架不能满足需求,我们想让框架来调用自己的类方法,怎么做呢?总不至于去修改框架吧。许多优秀的框架提几乎都供了相关的接口,我们只需要实现相关接口,即可完成了注册,然后在合适的时候让框架来调用我们自己的类。如果以类与类之间的关系方面去理解的话,A和B是关联关系的A调用B,同时传A给B。B执行完会调用A的方法(回调)

虽然A的方法本来可以自己完成,现在却交给B,然后让B回调自己的方法.。

A中的方法是一个暴漏给开发者的大众方法, 大家都可以使用A做事情。都可以往A中添加自己的个性化方法,但最终都要使用B完成回调。这样设计的好处之一就是, 把一些开发者在A中容易忘记或者一定会做的事情,强硬的放到了B中,比如数据的连接关闭,A需要去查询数据,首先得打开数据库连接,如果自己管理连接,有可能忘记关闭连接,回调就是把这些操作交给回调B去做,可以避免A忘记,确保了程序的健壮性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息