201709012工作日记--回调机制的理解
介绍回调函数之前先举个通俗易懂的例子(摘自知乎):https://www.zhihu.com/question/19801131
1.你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。
2.一般写程序是你调用系统的API,如果把关系反过来,你写一个函数,让系统调用你的函数,那就是回调了,那个被系统调用的函数就是回调函数。
下面我们用一个简单的程序详细解释一下毁掉函数:来自http://blog.csdn.net/xiaanming/article/details/8703708/
所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法,这样子说你是不是有点晕晕的,其实我刚开始也是这样不理解,看了人家说比较经典的回调方式:
- 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
我这个例子采用异步加回调 有一天小王遇到一个很难的问题,问题是“1 + 1 = ?”,就打电话问小李,小李一下子也不知道,就跟小王说,等我办完手上的事情,就去想想答案,小王也不会傻傻的拿着电话去等小李的答案吧,于是小王就对小李说,我还要去逛街,你知道了答案就打我电话告诉我,于是挂了电话,自己办自己的事情,过了一个小时,小李打了小王的电话,告诉他答案是2。
1 /** 2 * 这是一个回调接口 3 * 4 * 接口函数也就是回调函数 5 * 6 * @author nubia 7 * 8 */ 9 public interface CallBack { 10 public void solve(String result); 11 }
1 /** 2 * 首先小王实现了回调接口CallBack 3 * 4 * @author nubia 5 * 6 */ 7 public class Wang implements CallBack { 8 9 private Li li; 10 11 public Wang(Li li) { 12 this.li = li; 13 } 14 15 public void askQuestion(final String question) { 16 /* 17 * 这里用一个线程就是异步调用 new Thread(new Runnable() { 18 * 19 * @Override public void run() { // TODO Auto-generated method stub 20 * li.executeMessage(Wang.this, question); } }).start(); 21 */ 22 // 小王问完问题就干其他事去了,无需一直阻塞等待小李, 23 // 等小李有答案时会调用回调方法solve,告知小王答案 24 play(); 25 // 小王调用小李中的方法,在这里注册回调接口 26 li.executeMessage(this, question); 27 } 28 29 private void play() { 30 System.out.println("小王:小王问完问题接着玩儿 ~~~~~~"); 31 } 32 33 /** 34 * 小李知道答案后调用此方法告诉小王,就是所谓的小王的回调方法 根据小李告诉我的答案,我小王可以继续进行下面的工作啦,~~~ 35 */ 36 @Override 37 public void solve(String result) { 38 System.out.println("小王:小李给我的答案是 " + result); 39 } 40 41 }
1 public class Li { 2 /** 3 * 类B中要有参数为CallBack的方法 4 * 5 * @param callBack 6 * @param question 7 */ 8 public void executeMessage(CallBack callBack, String question) { 9 System.out.println("小李:小王发过来一个问题 " + question); 10 // 模拟小李办自己的事情需要很长时间 11 for (int i = 0; i < 100000; i++) { 12 13 } 14 // 小李办完自己的事情后经过计算得知答案 15 String result = "经过计算答案是 2"; 16 /** 17 * 于是就打电话告诉小王,调用小王中的方法 这就相当于B类反过来调用A的方法D 18 */ 19 callBack.solve(result); 20 } 21 }
1 public class Test { 2 public static void main(String[] args) { 3 Li li = new Li(); 4 Wang wang = new Wang(li); 5 /** 6 * 小王问小李问题 7 */ 8 wang.askQuestion("1+1=?"); 9 } 10 }
一张大图来了,灰常清晰的结构:

为了简单起见,把异步线程中的回调给注释掉了。
下面我们看看同步回调onClick()方法:
现在来分析分析下Android View的点击方法onclick();我们知道onclick()是一个回调方法,当用户点击View就执行这个方法,我们用Button来举例好了。
1 //这个是View的一个回调接口 2 /** 3 * Interface definition for a callback to be invoked when a view is clicked. 4 */ 5 public interface OnClickListener { 6 /** 7 * Called when a view has been clicked. 8 * 9 * @param v The view that was clicked. 10 */ 11 void onClick(View v); 12 }
1 import android.app.Activity; 2 import android.os.Bundle; 3 import android.view.View; 4 import android.view.View.OnClickListener; 5 import android.widget.Button; 6 import android.widget.Toast; 7 8 /** 9 * 这个就相当于Class A 10 * @author xiaanming 11 * 实现了 OnClickListener接口---->背景一 12 */ 13 public class MainActivity extends Activity implements OnClickListener{ 14 /** 15 * Class A 包含Class B的引用----->背景二 16 */ 17 private Button button; 18 19 @Override 20 public void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 button = (Button)findViewById(R.id.button1); 24 25 /** 26 * Class A 调用View的方法,而Button extends View----->A类调用B类的某个方法 C 27 */ 28 button.setOnClickListener(this); 29 } 30 31 /** 32 * 用户点击Button时调用的回调函数,你可以做你要做的事 33 * 这里我做的是用Toast提示OnClick 34 */ 35 @Override 36 public void onClick(View v) { 37 Toast.makeText(getApplication(), "OnClick", Toast.LENGTH_LONG).show(); 38 } 39 40 }
下面是View类的setOnClickListener方法,就相当于B类咯,只把关键代码贴出来
1 /** 2 * 这个View就相当于B类 3 * @author xiaanming 4 * 5 */ 6 public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { 7 /** 8 * Listener used to dispatch click events. 9 * This field should be made private, so it is hidden from the SDK. 10 * {@hide} 11 */ 12 protected OnClickListener mOnClickListener; 13 14 /** 15 * setOnClickListener()的参数是OnClickListener接口------>背景三 16 * Register a callback to be invoked when this view is clicked. If this view is not 17 * clickable, it becomes clickable. 18 * 19 * @param l The callback that will run 20 * 21 * @see #setClickable(boolean) 22 */ 23 24 public void setOnClickListener(OnClickListener l) { 25 if (!isClickable()) { 26 setClickable(true); 27 } 28 mOnClickListener = l; 29 } 30 31 32 /** 33 * Call this view's OnClickListener, if it is defined. 34 * 35 * @return True there was an assigned OnClickListener that was called, false 36 * otherwise is returned. 37 */ 38 public boolean performClick() { 39 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 40 41 if (mOnClickListener != null) { 42 playSoundEffect(SoundEffectConstants.CLICK); 43 44 //这个不就是相当于B类调用A类的某个方法D,这个D就是所谓的回调方法咯 45 mOnClickListener.onClick(this); 46 return true; 47 } 48 49 return false; 50 } 51 }
这个例子就是Android典型的回调机制,看完这个你是不是更进一步的理解了回调机制呢? 线程run()也是一个回调方法,当执行Thread的start()方法就会回调这个run()方法,还有处理消息都比较经典等等。
总结上面的例子可以看出整个回调过程是这样的:首先A需要实现一个借口,另外A需要含有B的引用,因此利用B的方法处理A的请求事件,需要传入A的引用,方便B反过来调用A中的回调方法,最终B将完成的工作结果通过A的引用传递给了A,A拿到结果后可以按照自己的方式处理自己的问题。这就是整个回调过程。
回调的目的:其实,回调函数就是在一个不确定实现的方法METHOD中用interface或者它的抽象方法留个口子,留给具体调用者(调用前边那个不确定的方法METHOD)在调用时提供具体实现来补上那个口子。从而达到更灵活地编码的目的,也大大减少了子类的使用。
在java里回调就是把某个对象的引用传递到另一个对象的方法里,且另一个对象方法的接收参数一般是某个对象的实现接口,所以另一个对象的方法里也只能调用到接口里的方法,不会暴露其他方法。


对比上面两个例子,实现过程完全是一模一样的,只是我们平时在使用系统回调时,回调函数不需要我们自己写罢了。回调比较好的理解可以参考下面几篇文章:
https://www.zhihu.com/question/19801131
http://blog.csdn.net/xiaanming/article/details/8703708/
http://blog.csdn.net/a_running_wolf/article/details/49359923/
http://blog.csdn.net/it_zjyang/article/details/51417001
浙公网安备 33010602011771号