在阅读基于Web QQ 协议的QQ机器人小薇的源码时,我发现作者大量使用了回调函数,我对Java回调机制并不十分了解,因此看的时候非常吃力,理解不了作者为什么要这样写,现在读了很多资料来写一些个人理解。
什么是回调
回调函数
回调函数就是通过函数指针调用函数吧函数的指针作为参数传给另外一个函数当这个指针被用来调用所指函数时,就称其为回调函数。但是Java是一门面向对象语言,没有指针这一说,所以采用接口来实现回调机制。
回调机制
回调机制把某个功能通过接口暴露给使用者,在一方初始化时,将函数都注册,接下来根据工作中遇到的不同情况选择回调响应函数。
使用场景
-
服务端不能决定所要执行的内容而要让给客户端实现
-
需要定时操作或条件操作又不想在函数体内实现太复杂的选择逻辑
异步回调简述(小薇源码解读)
异步回调需要使用多线程,其实用多线程解决的问题单线程一般也能解决,但是这是区分程序员是否具有更出色的解决问题的关键之一。多线程不用等待服务器响应即可继续执行,大大提升了代码运行的效率。
接口
import com.scienjus.smartqq.model.DiscussMessage;
import com.scienjus.smartqq.model.GroupMessage;
import com.scienjus.smartqq.model.Message;
/**
* 收到消息的回调
* @author ScienJus
* @date 2015/12/18.
*/
public interface MessageCallback {
/**
* 收到私聊消息后的回调
* @param message
*/
void onMessage(Message message);
/**
* 收到群消息后的回调
* @param message
*/
void onGroupMessage(GroupMessage message);
/**
* 收到讨论组消息后的回调
* @param message
*/
void onDiscussMessage(DiscussMessage message);
}
客户端
public class SmartQQClient implements Closeable {
//SmartQQ客户端的构造方法,传入一个信息传递接口的实现
public SmartQQClient(final MessageCallback callback) {
this.client = Client.pooled().maxPerRoute(5).maxTotal(10).build();
this.session = client.session();
login();
if (callback != null) {
this.pollStarted = true;
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (!pollStarted) {
return;
}
try {
pollMessage(callback);
} catch (RequestException e) {
// Ignore SocketTimeoutException
if (!(e.getCause() instanceof SocketTimeoutException)) {
LOGGER.error(e.getMessage());
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
}
}
}).start();
}
}
/**
* 登录
*/
private void login() {
getQRCode();
String url = verifyQRCode();
getPtwebqq(url);
getVfwebqq();
getUinAndPsessionid();
// for fixes 103
getFriendStatus();
}
/**
* 拉取消息
* @param callback 获取消息后的回调
*/
private void pollMessage(MessageCallback callback) {
LOGGER.debug("开始接收消息");
JSONObject r = new JSONObject();
r.put("ptwebqq", ptwebqq);
r.put("clientid", Client_ID);
r.put("psessionid", psessionid);
r.put("key", "");
Response<String> response = post(ApiURL.POLL_MESSAGE, r);
JSONArray array = getJsonArrayResult(response);
for (int i = 0; array != null && i < array.size(); i++) {
JSONObject message = array.getJSONObject(i);
String type = message.getString("poll_type");
if ("message".equals(type)) {
callback.onMessage(new Message(message.getJSONObject("value")));
} else if ("group_message".equals(type)) {
callback.onGroupMessage(new GroupMessage(message.getJSONObject("value")));
} else if ("discu_message".equals(type)) {
callback.onDiscussMessage(new DiscussMessage(message.getJSONObject("value")));
}
}
}
@Override
public void close() throws IOException {
this.pollStarted = false;
if (this.client != null) {
this.client.close();
}
}
}
服务端
public class QQService {
public void initQQClient() {
LOGGER.info("开始初始化小薇");
//创建一个客户端实例,并传入一个实现MassageCallback接口的匿名类
xiaoV = new SmartQQClient(new MessageCallback() {
@Override
public void onMessage(final Message message) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500 + RandomUtils.nextInt(1000));
final String content = message.getContent();
final String key = XiaoVs.getString("qq.bot.key");
if (!StringUtils.startsWith(content, key)) { // 不是管理命令,只是普通的私聊
// 让小薇进行自我介绍
xiaoV.sendMessageToFriend(message.getUserId(), XIAO_V_INTRO);
return;
}
final String msg = StringUtils.substringAfter(content, key);
LOGGER.info("Received admin message: " + msg);
sendToPushQQGroups(msg);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "XiaoV on group message error", e);
}
}
}).start();
}
@Override
public void onGroupMessage(final GroupMessage message) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500 + RandomUtils.nextInt(1000));
onQQGroupMessage(message);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "XiaoV on group message error", e);
}
}
}).start();
}
@Override
public void onDiscussMessage(final DiscussMessage message) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500 + RandomUtils.nextInt(1000));
onQQDiscussMessage(message);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "XiaoV on group message error", e);
}
}
}).start();
}
});
// Load groups & disscusses
reloadGroups();
reloadDiscusses();
LOGGER.info("小薇初始化完毕");
if (MSG_ACK_ENABLED) { // 如果启用了消息送达确认
LOGGER.info("开始初始化小薇的守护(细节请看:https://github.com/b3log/xiaov/issues/3)");
xiaoVListener = new SmartQQClient(new MessageCallback() {
@Override
public void onMessage(final Message message) {
try {
Thread.sleep(500 + RandomUtils.nextInt(1000));
final String content = message.getContent();
final String key = XiaoVs.getString("qq.bot.key");
if (!StringUtils.startsWith(content, key)) { // 不是管理命令
// 让小薇的守护进行自我介绍
xiaoVListener.sendMessageToFriend(message.getUserId(), XIAO_V_LISTENER_INTRO);
return;
}
final String msg = StringUtils.substringAfter(content, key);
LOGGER.info("Received admin message: " + msg);
sendToPushQQGroups(msg);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "XiaoV on group message error", e);
}
}
@Override
public void onGroupMessage(final GroupMessage message) {
final String content = message.getContent();
if (GROUP_SENT_MSGS.contains(content)) { // indicates message received
GROUP_SENT_MSGS.remove(content);
}
}
@Override
public void onDiscussMessage(final DiscussMessage message) {
final String content = message.getContent();
if (DISCUSS_SENT_MSGS.contains(content)) { // indicates message received
DISCUSS_SENT_MSGS.remove(content);
}
}
});
LOGGER.info("小薇的守护初始化完毕");
}
LOGGER.info("小薇 QQ 机器人服务开始工作!");
}
}
同步回调简述
在上述代码中,可以看到在客户端里作者采用了线程执行,这样就不必等服务端返回结果,这就是异步执行。同步执行的不同就在于它需要等服务端的响应,是阻塞的。