callback不在23中设计模式之中,但我觉得它和设计模式一样重要。
大家都知道异步IO比同步IO性能更好,唯一的问题就是异步IO不能马上获得返回结果。
一般通过一个callback来获取返回值。我们通过一个简单的异步IO例子,来理解下callback的应用。
首先我们实现一个客户端,它负责发送消息
public class AsyncHttpClient { private Connection connect(String host, int port) { return new Connection(host, port); } public void sendMsg(String host, int port, String msg, final InvokeCallback invokeCallback) { Connection connection = connect(host, port); Command command = new Command(msg); boolean sendOk = connection.send(command); ResponseFuture future = new ResponseFuture(command,invokeCallback); if (sendOk) { future.executeInvokeCallback(); } } public static void main(String[] args) { AsyncHttpClient httpClient = new AsyncHttpClient(); httpClient.sendMsg("hw-node4", 9090, "hello?", new InvokeCallback() { @Override public void operationComplete(ResponseFuture future) { String s = future.get(); System.out.println("receive="+s); } }); } }
消息的内容,我们包装了一个Command类
public class Command { private static final AtomicLong idGenerator = new AtomicLong(1); private Long requestId; private String msg; public Command(String msg) { this.requestId = idGenerator.getAndIncrement(); this.msg = msg; } @Override public String toString() { return "msg='" + msg ; } }
为了建立和服务端的连接,我们还需要一个Connection类
public class Connection { private String host; private int port; public Connection(String host, int port) { this.host = host; this.port = port; System.out.println("init Connection"); } boolean send(Command msg) { System.out.println("send=" + msg.toString()); try { Thread.sleep(1000); return true; } catch (InterruptedException e) { e.printStackTrace(); return false; } } }
因为是异步IO,所以发送完消息后,会生成一个Future对象。
理论上Future对象会跟服务端交互,在未来的某一个时刻拿到时间的response,但这里我们没有实际的服务端,所以就简单返回一个OK。
public class ResponseFuture { private final Command msg; private final InvokeCallback invokeCallback; public ResponseFuture(Command msg, InvokeCallback invokeCallback) { this.msg = msg; this.invokeCallback = invokeCallback; } public String get() { return msg + ",ok!"; } /** * 执行回调函数 */ void executeInvokeCallback() { invokeCallback.operationComplete(this); } }
回调接口也很简单,它给我们返回Future对象
public interface InvokeCallback { void operationComplete(ResponseFuture future); }
这样我们在sendMsg的同时,传入一个InvokeCallback,里面有一个operationComplete方法,方法的内容使我们自定义的。
一旦我们成功收到response,就会执行这个回调函数。
另外一种回调的使用场景是前端。
比如页面上设计了一个按钮,但按钮并不知道用户点击后需要自己干什么,所以还需要传入一个回调函数。里面包着需要做的事情
Button button = (Button)findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { System.out.println("I am clicked."); } });