buder

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

 

posted on 2017-09-12 10:17  buder  阅读(163)  评论(0)    收藏  举报

导航