Home  >  Article  >  Java  >  Detailed explanation of Java callback methods

Detailed explanation of Java callback methods

高洛峰
高洛峰Original
2017-01-24 11:46:331550browse

Callback is defined in Wikipedia as:

In computer programming, a callback function refers to a reference to a certain block of executable code that is passed to other code through function parameters.

The purpose is to allow low-level code to call subroutines defined at higher levels.

An example may make it clearer: take the use of retrofit to make network requests in Android as an example. This is an example of asynchronous callback.

After initiating a network request, the app can continue with other things. The results of the network request are generally returned through the onResponse and onFailure methods. Take a look at the relevant part of the code:

call.enqueue(new Callback<HistoryBean>() {
      @Override
      public void onResponse(Call<HistoryBean> call, Response<HistoryBean> response) {
        HistoryBean hb = response.body();
        if(hb == null) return;
        showText.append(hb.isError() + "");
        for(HistoryBean.ResultsBean rb : hb.getResults()){
          showText.append(rb.getTitle() + "/n");
        }
      }
      @Override
      public void onFailure(Call<HistoryBean> call, Throwable t) {
      }
    });

Ignore the generics in CallBack above. According to the definition in Wikipedia, all the code in the anonymous inner class can be regarded as function parameters passed to other codes. A certain block can A reference to the executing code. The two methods onResponse and onFailure are callback methods. The underlying code is the unchanged network request part that has been written, and the subroutine defined by the higher level is the callback. Because the specific implementation is left to the user, it has a high degree of flexibility. The above is related through the enqueue(Callback callback) method.

Steps of the callback method

The callback mentioned above is a very general concept. When it is put into program writing, it can be said:

Class A calls the method in class B A certain method C, and then class B in turn calls method D in class A, where D is the callback method. Class B is the low-level code, and Class A is the high-level code.

So through the above explanation, we can infer something. In order to express the versatility of the D method, we use the form of an interface to call the D method an interface method. Then if class B wants to call class A Method D, then class A must implement this interface. In this way, depending on the implementation, there will be polymorphism, making the method flexible.

If class A wants to call a method C in class B, class A must contain a reference to B, otherwise it cannot be called. This step is called registering the callback interface. So how do you implement class B to in turn call method D in class A? Directly through the above method C. Method C in class B accepts a parameter of an interface type, so you only need to use this interface type in method C. To call method D with the parameters, you can in turn call method D in class A in class B. This step is called calling the callback interface.

This also realizes that the C method in class B needs to call the D method in class A in turn. This is the callback. A calling B is a direct call, which can be seen as high-level code using the underlying API. We often write programs this way. B calling A is a callback, and the underlying API requires high-level code to execute.

Finally, to summarize, the steps of the callback method:

Class A implements the interface CallBack callback

Class A contains a reference to B

B There is a method f(CallBack callback) with a parameter of CallBack

Call the method f(CallBack callback) of B in class A - register the callback interface

B can call it in f(CallBack The method of calling A in the callback) method - calling the callback interface

Example of callback

Let's take a son playing a game and waiting for his mother to prepare the meal and then notify the son to eat it. Follow the above steps to write the callback;

In the above example, it is obvious that the son should implement the callback interface, and the mother should call the callback interface. So we first define a callback interface, and then let our son implement this callback interface.

The code is as follows:

public interface CallBack {
  void eat();
}
public class Son implements CallBack{
  private Mom mom;
  //A类持有对B类的引用
  public void setMom(Mom mom){
    this.mom = mom;
  }
  @Override
  public void eat() {
    System.out.println("我来吃饭了");
  }
  public void askMom(){
    //通过B类的引用调用含有接口参数的方法。
     System.out.println("饭做了吗?");
    System.out.println("没做好,我玩游戏了");
    new Thread(() -> mom.doCook(Son.this)).start();
    System.out.println("玩游戏了中......");
  }
}

Then we also need to define a mother class, which has a method doCook with interface parameters

public class Mom {
  //在含有接口参数的方法中利用接口参数调用回调方法
  public void doCook(CallBack callBack){
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          System.out.println("做饭中......");
          Thread.sleep(5000);
          callBack.eat();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();
  }
}

We pass a test class:

public class Test {
  public static void main(String[] args) {
    Mom mom = new Mom();
    Son son = new Son();
    son.setMom(mom);
    son.askMom();
  }
}

This example is a typical callback example. The Son class implements the callback method of the interface. It calls doCook in the Mom class through the askMom method to implement the registration callback interface, which is equivalent to the code C in class A that calls class B. The doCook in the Mom class calls back the eat in the Son class to tell the Son class the result.

In this way, we have implemented a simple callback that meets the definition.

Further exploration of callback examples

We mainly look at the code of the Son class:

public class Son implements CallBack{
  public Mom mom;
  public Son(Mom mom){
    this.mom = mom;
  }
  public void askMom(){
    System.out.println("饭做了吗?");
    System.out.println("没做好,我玩游戏了");
    new Thread(() -> mom.doCook(Son.this)).start();
    System.out.println("玩游戏了中......");
  }
  @Override
  public void eat() {
    System.out.println("好了,我来吃饭了");
  }
}

In this class, except In addition to outputting some statements, the really useful parts are mom.doCook(Son.this) and overriding the eat method. Therefore, we can abbreviate this callback in the form of an anonymous inner class. The code is as follows:

public class CallBackTest {
  public static void main(String[] args) {
    Mom mom = new Mom();
    new Thread(()-> mom.doCook(() -> System.out.println("吃饭了......"))).start();
  }
}

Cancel the Son class and implement the eat method directly through the anonymous inner class in the main method. In fact, anonymous inner classes are the embodiment of callbacks.

Asynchronous callback and synchronous callback

Callback As we said above, A calls method C in class B, and then calls method D in class A through the object of class A in method C. .

We are talking about asynchronous and synchronic, let’s talk about the concept of synchronization first

Synchronization

Synchronization refers to when calling a method, if the previous method call has not been completed , it is impossible to make new method calls. That is to say, things must be done one by one. After finishing the previous one, you can do the next one.

Asynchronous

Compared with synchronization, asynchronous does not need to wait for the previous method call to end before calling a new method. Therefore, in an asynchronous method call, a method is needed to notify the user of the result of the method call.

How to achieve asynchronous implementation

在Java中最常实现的异步方式就是让你想异步的方法在一个新线程中执行。

我们会发现一点,异步方法调用中需要一个方法来通知使用者调用结果,结合上面所讲,我们会发现回调方法就适合做这个事情,通过回调方法来通知使用者调用的结果。

那异步回调就是A调用B的方法C时是在一个新线程当中去做的。

上面的母亲通知儿子吃饭的例子,就是一个异步回调的例子。在一个新线程中,调用doCook方法,最后通过eat来接受返回值,当然使用lamdba优化之后的,本质是一样的。

同步回调就是A调用B的方法C没有在一个新线程,在执行这个方法C的时候,我们什么都不能做,只能等待他执行完成。

同步回调与异步回调的例子

我们看一个Android中的一个同步回调的例子:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       Log.i("button","被点击");
    }
});

button通过setOnClickListener注册回调函数,和上面写的一样,通过匿名内部类的形式将接口的引用传进去。由于button调用setOnClickListener没有新建一个线程,所以这个是同步的回调。

而异步回调,就是我们开篇讲的那个例子:

call.enqueue(new Callback<HistoryBean>() {
      @Override
      public void onResponse(Call<HistoryBean> call, Response<HistoryBean> response) {
        HistoryBean hb = response.body();
        if(hb == null) return;
        showText.append(hb.isError() + "");
        for(HistoryBean.ResultsBean rb : hb.getResults()){
          showText.append(rb.getTitle() + "/n");
        }
      }
      @Override
      public void onFailure(Call<HistoryBean> call, Throwable t) {
      }
    });

   

这个enqueue方法是一个异步方法去请求远程的网络数据。其内部实现的时候是通过一个新线程去执行的。

通过这两个例子,我们可以看出同步回调与异步回调的使用其实是根据不同的需求而设计。不能说一种取代另一种,像上面的按钮点击事件中,如果是异步回调,用户点击按钮之后其点击效果不是马上出现,而用户又不会执行其他操作,那么会感觉很奇怪。而像网络请求的异步回调,因为受限于请求资源可能不存在,网络连接不稳定等等原因导致用户不清楚方法执行的时候,所以会用异步回调,发起方法调用之后去做其他事情,然后等回调的通知。

回调方法在通信中的应用

上面提到的回调方法,除了网络请求框架的回调除外,其回调方法都是没有参数,下面,我们看一下在回调方法中加入参数来实现一些通信问题。

如果我们想要A类得到B类经过一系列计算,处理后数据,而且两个类是不能通过简单的将B的引用给A类就可以得到数据的。我们可以考虑回调。

步骤如下:

在拥有数据的那个类里面写一个回调的接口。-->这里就是B类中写一个回调接口

回调方法接收一个参数,这个参数就是要得到的数据

同样是在这个类里写一个注册回调的方法。

在注册回调方法,用接口的引用去调用回调接口,把B类的数据当做参数传入回调的方法中。

在A类中,用B类的引用去注册回调接口,把B类中的数据通过回调传到A类中。

上面说的步骤,有点抽象。下面我们看一个例子,一个是Client,一个是Server。Client去请求Server经过耗时处理后的数据。

public class Client{
  public Server server;
  public String request;
  //链接Server,得到Server引用。
  public Client connect(Server server){
    this.server = server;
    return this;
  }
  //Client,设置request
  public Client setRequest(String request){
    this.request = request;
    return this;
  }
  //异步发送请求的方法,lamdba表达式。
  public void enqueue(Server.CallBack callBack){
    new Thread(()->server.setCallBack(request,callBack)).start();
  }
}
public class Server {
  public String response = "这是一个html";
  //注册回调接口的方法,把数据通过参数传给回调接口
  public void setCallBack(String request,CallBack callBack){
    System.out.println("已经收到request,正在计算当中......");
    new Thread(() -> {
      try {
        Thread.sleep(5000);
        callBack.onResponse(request + response);
      } catch (InterruptedException e) {
        e.printStackTrace();
        callBack.onFail(e);
      }
    }).start();
  }
  //在拥有数据的那个类里面写一个接口
  public interface CallBack{
    void onResponse(String response);
    void onFail(Throwable throwable);
  }
}

   

接下来,我们看一下测试的例子:

public class CallBackTest {
  public static void main(String[] args) {
    Client client = new Client();
    client.connect(new Server()).setRequest("这个文件是什么?").enqueue(new Server.CallBack() {
      @Override
      public void onResponse(String response) {
        System.out.println(response);
      }
      @Override
      public void onFail(Throwable throwable) {
        System.out.println(throwable.getMessage());
      }
    });
  }
}

   

结果如下:

已经收到request,正在计算当中......
这个文件是什么?这是一个html

  

以上就是通过回调的方式进行通信。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持PHP中文网!

更多Java回调方法详解相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn