文章目录
回调机制的简单的理解:
A类通过某函数实现功能,但A的执行却并不取决于A,而是由B来决定。这时通常将函数的入口地址当作参数传递到B类
比较经典的回调方式:
A的回调方法是由B中的方法调用的,使用时传入回调方法即可。
————————————————————————————————————————
class A实现接口CallBack callback
class A中关联一个class B的引用b //此步很关键,相当于classA注册classB
class B有一个参数为callback的方法f(CallBack callback)
A调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C
然后B就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
————————————————————————————————————————
示例
现有一农场需要向气象局订阅天气预报信息。农场向气象局发出订阅请求,气象局接受农场的订阅请求后,
每天都会向农场推送后一天的天气信息。农场每天接受到天气预报信息,将做对应的生产安排,具体安排
如下:如果气温在010℃,播种小麦,如果气温在1115℃播种大豆,如果气温在16~20℃播种棉花,否则
维护农场设备。
我们从“案例1”中可以提取回调的概念。
农场接收到天气预报信息后,才会进行“工作安排”,由于农场不知道天气预报信息返回的精确时间,因此进行
“工作安排”的时机实际是由气象局决定的。自然地,我们想到将“工作安排”用一个函数(func())来实现,并且该函数的
具体实现由农场(Farm类)来实施,而函数的调用位置及调用时机由气象局(MeteorologicalBureau类)来决定。这就是一个典型的回调场景,
监听模型分析:气象局发送天气信息为事件源
天气信息为事件
农场接收消息并做相应处理为监听器
回调模型分析:农场做工作安排,但工作安排是依赖于气象局发送的天气好坏的决定的,所以工作安排就是回调函数.
结论:农场实现了回调函数,通过传递回调接口到气象局后,回调函数调用时机是气象局来决定的
也就是说,通过农场将回调函数传递给气象局,实现了气象局预测天气信息——触发执行——>农场的回调函数
基于监听的事件处理
JAVA事件:根据不同的事件类型调用不同的处理函数
监听机制是a发生事件触发多个监听器bcd来执行事件处理函数
## 气象局发布气象信息:事件源
public class 气象局{
农场 nc;
气象局(农场 nc){ //注册观察者
this.nc=nc;
}
void predirect(){
new Thread(new Runnable(){
public void run(){
try{
Thread.sleep(10000); //模拟耗时操作
}catch(Exception e){
e.printStackTrace();
}
maxTemperature=100;
minTemperature=1;
syso("气象局发布天气信息:最高气温:"+maxTemperature+"最低气温"+minTemperature);
nc.call(maxTemperature,minTemperature); //工作安排
}
}).start();
}
}
## 农场为事件监听器:监听天气信息,进行工作安排
public class 农场{
//定义回调函数
void call(int maxTemperature,int minTemperature){
//根据不同的温度做不同的工作安排
}
}
public static void main(){
//1. 监听器,监听气象局的气象信息
农场 nc=new 农场();
//2. 事件源注册监听器
气象局 qxj=new 气象局(nc);
//3. 气象局进行预测
qxj.predirect();
}
回调的简单理解
假设有两个类,类A有一个方法a(),类B有一个方法b(),类A调用类B的方法b(),而方法b()回调了方法a()
回调就想一个约定,如果我们调用了b,那么就必须要回调,而不需要显示调用
我们用例子来解释:小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。一起去吃饭这个事件就是方法a(),小李去洗漱就是方法b()。
同步回调
public interface Callable{
public void togetherEat(String food); //定义回调接口
}
小明
public class XiaoMing implements EatRice{//小明
//小李先洗脸,然后小明和小李一起吃
public void eatFood() {
XiaoLi xl = new XiaoLi();
//A调用B的方法,则自动回调A的方法
xl.washFace("大龙虾", this);//this指的是小明这个类实现的EatRice接口
}
//重写回调函数
@Override
public void eat(String food) {
// TODO Auto-generated method stub
System.out.println("小明和小李一起去吃" + food);
}
}
小李
public class XiaoLi{
public void washFace(String food,EatRice er){
System.out.println("小李要洗漱");
//B回调A的方法
er.eat(food);
}
}
测试Demo
public class demo {
public static void main(String args[]) {
XiaoMing xm = new XiaoMing();
xm.eatFood();
}
}
异步回调函数
小李洗脸的时候,小明玩手机
public interface Callable{
public void togetherEat(String food); //定义回调接口
}
小明
public class XiaoMing implements EatRice{//小明
//小李先洗脸,然后小明和小李一起吃
public void eatFood() {
XiaoLi xl = new XiaoLi();
//A调用B的方法,则自动回调A的方法
xl.setEatRiceListener(this, "大龙虾"); //this指的是小明这个类实现的EatRice接口
}
//重写回调函数
@Override
public void togetherEat(String food) {
// TODO Auto-generated method stub
System.out.println("小明和小李一起去吃" + food);
}
}
小李
public class XiaoLi{
private EarRice er;
public void setEatRiceListener(EatRice er, String food) {
this.er = er;
washFace(food);
}
public void washFace(String food,EatRice er){
System.out.println("小李要洗漱");
//B异步回调A的方法
new Thread(new Runnable(){
// TODO Auto-generated method stub
try {
//小李洗漱的同时小明玩手机,开启线程实现异步
play();
Thread.sleep(10000);
System.out.print("10秒后 ");
//B调用A的方法
er.eat(food);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
er.togetherEat(food);
}
//小明玩手机
public void play() {
System.out.println(" 小明要玩手机");
}
}
测试Demo
public class demo {
public static void main(String args[]) {
XiaoMing xm = new XiaoMing();
xm.eatFood(); //类A调用类B的方法,自动会调用回调函数
}
}
Java回调机制进阶
假设有两个类,类A有一个方法a(),类B有一个方法b(),类A调用类B的方法b(),而方法b()回调了方法a()。
深入理解:
类A实现回调方法a(),但a的调用却依赖于类B方法的调用类A调用类B的方法,自动会调用回调函数

结构
class A实现接口CallBack callback
class A中关联一个class B的引用b
class B有一个带callback的参数,一般会传入this
操作
A调用B带有callback参数的方法(CallBack callback) //A把回调函数注册/传递给B,让B进行调用
然后B就可以在f(CallBack callback) //调用回调函数
## 1. 声明天气预报发布接口,通过内部实现类订阅和发布
## 2. 创建气象局,该类接受天气预告信息的订阅和发布天气预报信息
## 3. 创建农场类,该类订阅天气预报信息,并在接受到天气预报信息后做工作安排
将内部类对象传递给气象局对象==将farm类的onRelease()传递给气象局对象
## 4. 编写测试类,首先new一个农场对象并订阅天气预报信息,然后气象局调用predict()函数预测天气并发布预报
## 5. 运行,结果如下:
2019-02-06 21-54-59,农场订阅天气预报信息
2019-02-06 21-55-08,农场接收到天气信息:明日渝北区气温8~10℃,风力5~8级
2019-02-06 21-55-08,农场明日工作安排:播种小麦
分析:工作安排依赖于天气预报信息,所以定义它为回调函数
代码实现
## 总目标:实现天气预告信息的订阅和发布,天气预告信息发布后做工作安排
## 订阅:农场的发布监听器对象传入气象局,实例化用于预测天气的类。但是预测数据任然是气象局提供的
## 定义回调函数
public interface Callable{
void doAfterReceiveWheatherInfo(int minTemperature, int maxTemperature);
}
public class Farm implements Callable{
private MeteorologicalBureau mBureau; //气象局
private MeteorologicalBureauListener mBureauListener; //这个农场注册用的,将其注册到气象局==气象局有了发布方法
public Farm(MeteorologicalBureau bureau){
mBureau = bureau;
}
/**
* 订阅天气信息
*/
public void subscribe(){
System.out.println(TestUtils.getTimeStamp() + "," + "农场订阅天气预报信息");
mBureauListener = new MeteorologicalBureauListener() {
public void onRelease(String description, int minTemperature,
int maxTemperature, int minWindscale, int maxWindscale) {
System.out.println(TestUtils.getTimeStamp() + ","
+ "农场接收到天气信息:" + description);
}
};
mBureau.register(mBureauListener); //通过一个发布对象,注册农场到气象局
mBureau.setProperty(Callable call); //调用类B的b方法,将回调函数传入
}
/**
* 接收到气象局发布的天气信息后,农场做对应的工作安排
* @param minTemperature 明日最低气温
* @param maxTemperature 明日最高气温
* 调用时机取决于气象局,农场类只负责
*/
private void doAfterReceiveWheatherInfo(int minTemperature, int maxTemperature){
String timeStamp = TestUtils.getTimeStamp();
if(minTemperature >= 0 && minTemperature <= 10){
System.out.println(timeStamp + "," + "农场明日工作安排:播种小麦");
}else if(minTemperature >= 11 && minTemperature <= 15){
System.out.println(timeStamp + "," + "农场明日工作安排:播种大豆");
}else if(minTemperature >= 16 && minTemperature <= 20){
System.out.println(timeStamp + "," + "农场明日工作安排:播种棉花");
}else{
System.out.println(timeStamp + "," + "农场明日工作安排:维护设备");
}
}
}
## 声明天气预报发布接口,通过内部实现类订阅和发布
public interface MeteorologicalBureauListener {
void onRelease(String description, //天气预报信息描述
int minTemperature,
int maxTemperature,
int minWindscale, //最低风力
int maxWindscale); //最高风力
}
## 定义气象局-类B
/**
* 气象局(天气预报信息发布者)
* @author WenYong
*/
public class MeteorologicalBureau {
private MeteorologicalBureauListener mListener; //提供订阅发布功能
private Callable call;
void setProperty(Callable call){
this.call=call;
}
/**
* 注册对"气象局"类监听
* @param listener
*/
public void register(MeteorologicalBureauListener listener){
if(null == listener){
return;
}
mListener = listener;
}
/**
* 取消订阅天气信息
*/
public void unsubscribe(){
mBureau.unregister(mBureauListener);
}
/**
* 取消注册对"气象局"类监听
* @param listener
*/
public void unregister(MeteorologicalBureauListener listener){
if(null == listener){
return;
}
if(mListener.equals(listener)){
mListener = null;
}
}
/**
* 天气信息预测
*/
public void predict(){
new Thread(new Runnable() {
public void run() {
try {
// 模拟耗时操作
Thread.sleep(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mListener.onRelease("明日渝北区气温8~10℃,风力5~8级", 8, 10, 5, 8);
}
}).start();
call.doAfterReceiveWheatherInfo(); //天气预告发布后,调用回调函数
}
}
## Demo测试
public class Test {
public static void main(String[] args) {
// 创建气象局
MeteorologicalBureau bureau = new MeteorologicalBureau();
// 农场订阅天气信息,注入listener
new Farm(bureau).subscribe();
// 气象局进行天气信息预测,自动调用回调函数
bureau.predict();
}
}
回调进阶(基于回调的"观察者"模式的实现)
在学会了回调的基本使用方法后,我们将案例1稍加修改,增加一个天气预报订阅者
案例2
现有一农场和一机场需要向气象局订阅天气预报信息。农场和机场向气象局发出订阅请求,气象局接受订阅请求后,
每天都会向农场和机场推送后一天的天气信息。农场每天接受到天气预报信息,将做对应的生产安排,具体安排
如下:如果气温在010℃,播种小麦,如果气温在1115℃播种大豆,如果气温在16~20℃播种棉花,否则
维护农场设备;机场接收到天气预报信息,将采取对应的运营管理措施,具体如下:如果风力小于5级,不做预警正常起飞,
如果风力5~8级,预警起飞,如果风力大于8级,暂停起飞。

观察者模式:农场和机场作为“观察者”,向气象局订阅天气预报信息
气象局作为“目标着”提供天气预报信息
创建气象局类MeteorologicalBureau,该类负责接受天气预报信息的订阅和发布天气预报信息
并发队列来存储机场和农场传递过来的MeteorologicalBureauListener实现对象的引用
## 创建机场类
/**
* 机场(天气预报信息订阅者)
* @author WenYong
*
*/
public class Airport extends Callable{
private MeteorologicalBureau mBureau; //订阅天气的气象局
private MeteorologicalBureauListener mBureauListener; //订阅天气的
public Airport(MeteorologicalBureau bureau){
mBureau = bureau;
}
/**
* 订阅天气信息
*/
public void subscribe(){
System.out.println(TestUtils.getTimeStamp() + "," + "机场订阅天气预报信息");
mBureauListener = new MeteorologicalBureauListener() {
public void onRelease(String description, int minTemperature,
int maxTemperature, int minWindscale, int maxWindscale) {
System.out.println(TestUtils.getTimeStamp() + ","
+ "机场接收到天气信息:" + description);
//doAfterReceiveWheatherInfo(minWindscale, maxWindscale);
}
};
mBureau.register(mBureauListener);
mBureau.setProperty(Callable call);
}
/**
* 取消订阅天气信息
*/
public void unsubscribe(){
mBureau.unregister(mBureauListener);
}
/**
* 接收到气象局发布的天气信息后,机场做对应的运营管理措施
* @param minWindscale
* @param maxWindscale
*/
private void doAfterReceiveWheatherInfo(int minWindscale, int maxWindscale){
String timeStamp = TestUtils.getTimeStamp();
if(maxWindscale < 5){
System.out.println(timeStamp + "," + "机场明日运营管理措施:不做预警正常起飞");
}else if(minWindscale >= 5 && maxWindscale <= 8){
System.out.println(timeStamp + "," + "机场明日运营管理措施:预警起飞");
}else{
System.out.println(timeStamp + "," + "机场明日运营管理措施:暂停起飞");
}
}
}
/**
* 气象局(天气预报信息发布者)
* @author WenYong
*
*/
public class MeteorologicalBureau {
private ConcurrentLinkedQueue<MeteorologicalBureauListener> mListenerQueue;
private Callable call;
public MeteorologicalBureau(){
mListenerQueue = new ConcurrentLinkedQueue<>();
}
void setProperty(Callable call){
this.call=call;
}
/**
* 注册对"气象局"类监听
* @param listener
*/
public void register(MeteorologicalBureauListener listener){
if(null == listener){
return;
}
if(mListenerQueue.contains(listener)){
return;
}
mListenerQueue.add(listener);
}
/**
* 取消注册对"气象局"类监听
* @param listener
*/
public void unregister(MeteorologicalBureauListener listener){
if(null == listener){
return;
}
if(!mListenerQueue.contains(listener)){
return;
}
mListenerQueue.remove(listener);
}
/**
* 天气信息预测
*/
public void predict(){
new Thread(new Runnable() {
public void run() {
try {
// 模拟耗时操作
Thread.sleep(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
release(mListenerQueue, "明日渝北区气温8~10℃,风力5~8级", 8, 10, 5, 8);
}
}).start();
this.call.doAfterReceiveWheatherInfo();
}
/**
* 天气预报信息发布
* @param queue 监听队列
* @param description 天气预报描述
* @param minTemperature 最低气温
* @param maxTemperature 最高气温
* @param minWindscale 最低风力
* @param maxWindscale 最高风力
*/
private void release(ConcurrentLinkedQueue<MeteorologicalBureauListener> queue,
String description,
int minTemperature,
int maxTemperature,
int minWindscale,
int maxWindscale){
if(null == queue || queue.isEmpty()){
return;
}
Iterator<MeteorologicalBureauListener> it = queue.iterator();
while(it.hasNext()){
it.next().onRelease(description, minTemperature,
maxTemperature, minWindscale, maxWindscale);
}
}
}
## Demo测试类
public class Test {
public static void main(String[] args) {
MeteorologicalBureau bureau = new MeteorologicalBureau();
// 农场订阅天气信息
new Farm(bureau).subscribe();
// 机场订阅天气信息
new Airport(bureau).subscribe();
// 气象局进行天气信息预测
bureau.predict();
}
}
总结:农场订阅天气信息时,可以将回调函数写到发布函数中,直到predict才会真正调用到发布函数。
回调机制和监听机制实现事件触发功能的区别
共同点都实现事件触发的功能
监听机制是气象局关联多个农场,当预测天气时调用工作函数 气象局关联多个农场,当发生事件循环通知多个农场
事件机制的简单的理解
引发的事件不由引发事件的对象自己处理,而是委派给独立事件处理对象负责。观察者模式的委派事件模型
将事件监听器注册到按钮上——>我单机按钮触发ActionEvent事件——>将事件传递给事件监听器并进行相应处理
事件处理模型中,主要涉及三类对象:
1.EventSource(事件源)。事件发生的场所,通常就是各个组件,比如按钮、窗口、菜单。
2.Event(事件)。事件封装了事件发生的相关信息。
3.EventListener(事件监听器)。监听事件源发生的事件,并对事件作出相应的响应。
事件驱动编程
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。
epoll原理
epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
基于监听的事件处理&基于回调的事件处理
Android提供了两套事件处理机制:
1.基于监听的事件处理。
2.基于回调的事件处理。
基于监听的事件处理就是在android的组件上绑定特定的监听器
基于回调的事件处理就是重写UI组件或者Activity的回调方法。
基于回调的事件处理用于处理一些具有通用性的事件,基于监听的事件处理用于处理与具体业务相关的事件。
总结:回调机制和监听机制的实质都是触发的一种实现,回调机制通过b给a传递回调接口,实现了a发生事件触发b的回调接口执行。而监听机制是a发生事件触发多个监听器bcd来执行事件处理函数。也就是说,回调机制本质上是一种特殊的监听机制
基于监听的事件处理是一种委派式(Delegation)的事件处理方式。UI组件(事件源)将发生的事件委派给特定的对象(监听器)处理,这很类似于人类社会的分工协作。例如某商场发生火灾时,商场并不会处理该事件,而是交给消防局处理;商场发生斗殴事件时,就交给公安局处理。商场可以将不同的事件委派给不同的机构处理,同时消防局和公安局也可以对各种场所进行监听并且处理发生的事件。这种委派式的事件处理方式将事件源和监听器分离,能提高程序的可维护性。

被折叠的 条评论
为什么被折叠?



