一、简介
所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法。
举个生活中的例子:我打电话向你请教问题,是个难题,你一时想不出解决方法,于是我们约定:等你想出办法后打手机通知我,我就挂掉电话办其它事情去了。过了一段时间,你打过来告诉我答案。
C在调用a后是不会自己调用b的,C提供b的目的就是让S来调用。但是S并不知道C提供的b是什么,因此需要约定统一的接口规范。
总结一下:
- Class A实现接口CallBack callback——背景1
- class A中包含一个class B的引用b ——背景2
- class B有一个参数为callback的方法f(CallBack callback) ——背景3
- A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法C
- 然后b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
上文举的生活中的例子中:你后来打手机告诉我结果就是一个回调过程,我的手机号码有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。
二、详述
安卓中很多地方都用到了回调机制:
场景一:
Android View的点击方法onclick()方法
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(newButton.OnClickListener() {
//回调函数
@override
public void onClick(View v) {
buttonTextView.setText("按钮被点击了");
}
});
//这个是View的一个回调接口
/**
* 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(View v);
/**
* View类,就相当于B类,贴出关键代码
*
*/
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnClickListener mOnClickListener;
/**
* setOnClickListener()的参数是OnClickListener接口------>背景三
* 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
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
/**
* Call this view's OnClickListener, if it is defined.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//这个不就是相当于B类调用A类的某个方法D,这个D就是所谓的回调方法咯
mOnClickListener.onClick(this);
return true;
}
return false;
}
}
场景一:
Activity的整个生命周期基本上都说回调函数在发挥作用
@Override
public void onCreate(BundlesaveInstanceState) {
super.onCreate(saveInstanceState);
// You code...
}
@Override
public void onResume() {
super.onResume();
// You code...
}
下面再模拟打电话例子的代码:
/**
* 回调接口(规范)
*/
public interface CallBack {
/**
* 这个是小李知道答案时要调用的函数告诉小王,也就是回调函数
* @param result 是答案
*/
public void solve(String result);
}
/**
* 相当于服务端S class B
*
* @author ReaganZhu
* @email 2668526325@qq.com
* @date 2020/8/17 10:39
*/
public class You implements Runnable {
private String who;
private CallBack callBack;
//class B有一个参数为callback的方法f(CallBack callback) ——背景3
public void setCallBack(String who,String question,CallBack callBack) {
this.who = who;
System.out.println("你说:当前联系到我的人是"+who+",问题是"+question);
this.callBack =callBack;
}
public void handleThings(){
//假如你现在正在想问题的答案,需要一点时间
for(int i=0;i<100000;i++){
if(i == 0){
System.out.println("你正在思考问题.....");
}
}
String answer = "答案是A";
//想到问题的办法了
System.out.println("你说:想到答案了,准备打回去给"+who+"告诉他答案");
//b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
callBack.callBackByTel(answer);
}
@Override
public void run() {
try {
Thread.sleep(1000);
handleThings();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 相当于客户端C class A
*
* @author ReaganZhu
* @email 2668526325@qq.com
* @date 2020/8/17 22:48
*/
public class Me {
public static void main(String[] args){
Me me = new Me();
//A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C
me.hasQuestion();
}
private void hasQuestion(){
//现在有问题想不出来答案,想去问你
//class A中包含一个class B的引用b ——背景2
You you = new You();
//Class A实现接口CallBack callback——背景1
you.setCallBack("蜗牛", "某道题答案是什么?", new CallBack() {
@Override
public void callBackByTel(String answer) {
System.out.println("我说:嗯,好的,我收到答案了:"+answer+",谢谢");
}
});
//你接到电话,起床开始思考问题
new Thread(you).start();
}
}
//运行结果:
你说:当前联系到我的人是蜗牛,问题是某道题答案是什么?
你正在思考问题…
你说:想到答案了,准备打回去给蜗牛告诉他答案
我说:嗯,好的,我收到答案了:答案是A,谢谢
其实线程run()也是一个回调方法,当执行Thread的start()方法就会回调这个run()方法,还有处理消息都比较经典等等
三、小结
程序的本质就是代码跳转,不管同步异步反射接口虚函数,本质上都是函数调用。函数我们要调用它,就需要它的指针,不同语言通过不同的方式来得到这个指针。而我们定义的接口其实就是一个函数指针,那么那个注册过程,其实就是相当于对那个函数指针赋值。通过这个函数指针来调用我们定义的自实现函数。