Event事件监听使用方法
环境: solidity 0.8.0, fisco节点 V3.2, java SDK V3.2, WeBASE V3.0.2
JavaSDK文档–接口说明: 已过时,代码不可用
https://fisco-bcos-doc.readthedocs.io/zh_CN/latest/docs/sdk/java_sdk/event_sub.html
参考代码:https://github.com/FISCO-BCOS/java-sdk-demo/blob/main/src/main/java/org/fisco/bcos/sdk/demo/eventsub/EventSub.java
1 监听工作流程介绍
客户端向节点发送注册请求,在请求中携带客户端关注的合约事件的参数,节点根据请求参数对请求区块范围的Event Log进行过滤,将结果分次推送给客户端。核心代码如下:
// 参数设置
EventSubParams params = new EventSubParams();
// 定义事件回调函数
EventSubCallback callback = new SubscribeCallback();
// 注册事件
EventSubscribe eventSubscribe = EventSubscribe.build(client);
String registerId = eventSubscribe.subscribeEvent(params, callback);
//停止监听事件
eventSubscribe.unsubscribeEvent( registerId );
eventSubscribe.subscribeEvent()是个非阻塞型函数,调用后返回注册id,之后才源源不断地接收到事件消息。
2 事件参数介绍
2.1 定义监听区块范围
参数params设置起始区块高度,
toBlock默认是BigInteger.valueOf(-1),表示latest,一直等待最新区块。
//监听1~300区块的事件
params.setFromBlock(BigInteger.valueOf(1));
params.setToBlock(BigInteger.valueOf(300));
2.2 定义监听事件
参数params设置监听主题topics,是双层数组形式, 第一层最多4个,第二层数量不限
[
[“topic1”,“topic2”,“topic3”,…],
[“topic1”,“topic2”,“topic3”,…],
[“topic1”,“topic2”,“topic3”,…],
[“topic1”,“topic2”,“topic3”,…]
]
// topics默认是为空数组,可以匹配所有的Event
topics = new ArrayList<>(Arrays.asList(null, null, null, null));
//手动设置为空
List<List<String>> topics = new ArrayList<List<String>>() {
{add(null);add(null);add(null);add(null);}};
//添加事件topic
params.addTopic(0,"0x2e601d02b417e3eebb1bf0305fed4a7196340cae7656e10ec6c8c1c16eb69f6c");
params.addTopic(0,"0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d");
params.addTopic(1,"0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d");
2.3 定义监听地址
参数params设置监听地址,是数组形式,允许同时监听多个地址。
默认为空数组,表示监听所有的合约地址。
// addresses默认为空数组,匹配所有的合约地址
params.addAddress("0x3705a5eac4741c6877f3a507468f07aef3394d65");
3 示例代码
场景1 将链上所有/最新的事件回调至客户端
读取所有区块的事件,持续监听最新区块。 会显示出所有的事件信息。
public void callAllEvents() throws JniException {
// 参数设置
EventSubParams params = new EventSubParams();
// 全部Event fromBlock设置为1
params.setFromBlock(BigInteger.valueOf(1));
// toBlock默认是-1,表示"latest",处理至最新区块继续等待新的区块
//params.setToBlock(BigInteger.valueOf(300));
// addresses默认为空数组,匹配所有的合约地址
//params.addAddress("0x3705a5eac4741c6877f3a507468f07aef3394d65");
// topics默认为空数组,匹配所有的Event
//params.addTopic(0, "0x2e601d02b417e3eebb1bf0305fed4a7196340cae7656e10ec6c8c1c16eb69f6c");
//params.addTopic(0, "0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d");
// 定义事件回调函数
EventSubCallback callback = new SubscribeCallback();
// 注册事件
EventSubscribe eventSubscribe = EventSubscribe.build(client);
String registerId = eventSubscribe.subscribeEvent(params, callback);
System.out.println("registerId="+registerId);
//停止监听事件
eventSubscribe.unsubscribeEvent( registerId );
}
//回调类
class SubscribeCallback implements EventSubCallback {
SubscribeCallback() {
}
@Override public void onReceiveLog(String eventSubId, int status, List<EventLog> logs) {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
}
}
运行结果:
registerId=b5e8113bbdd5481381632c1dcf66fd17
event sub id: b5e8113bbdd5481381632c1dcf66fd17
status: 0
logs: null
event sub id: b5e8113bbdd5481381632c1dcf66fd17
status: 0
logs: [EventLog{logIndex='0', transactionIndex='0', transactionHash='0xd1860249407a464d4fe5a6a3a4e5b3388649f6512a5941b99be5f2d266d4b6c8', blockNumber='5', address='6849f21d1e455e9f0712b1e99fa4fcd23758e8f1', data='0x000000000000000000000000a0b0d1cd6a783c0cd2f06613de0795356f4bb85800000000000000000000000000000000000000000000000000000000000000800000000000000000000000001c7a618fa0388aa34bb19081819ea2723b671a96000000000000000000000000437bc19334f027fd90fd39d08fc138962f51d00200000000000000000000000000000000000000000000000000000000000000056170706c65000000000000000000000000000000000000000000000000000000', topics=[0x03b9f246c94041ffa51bb187f9dbb224690a033d125054ccbcb1c17ef6c9b68b]}]
省略若干消息。。。。
event sub id: b5e8113bbdd5481381632c1dcf66fd17
event sub id: b5e8113bbdd5481381632c1dcf66fd17
status: 0
status: 0
logs: [EventLog{logIndex='0', transactionIndex='0', transactionHash='0xc5ebcba146269a0f0e133e33e2bf9935be595efb0a96eb7531c39a61307a3240', blockNumber='308', address='4035a74c5f0daead4ae48a9be0415cfaa8a69d34', data='0x000000000000000000000000a0b0d1cd6a783c0cd2f06613de0795356f4bb8580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000e8aed5848f12dee58599df4f95e7a584ca895d7500000000000000000000000000000000000000000000000000000000000000056170706c65000000000000000000000000000000000000000000000000000000', topics=[0xa2d66134357ca61a7cfe28b780322e6451179110b2348db711cc2ce737c1e6b7]}]
logs: [EventLog{logIndex='0', transactionIndex='0', transactionHash='0x65f14efc743b227b131da2b881d751f77a6b644baafce145b0255692d132e86f', blockNumber='307', address='4035a74c5f0daead4ae48a9be0415cfaa8a69d34', data='0x000000000000000000000000a0b0d1cd6a783c0cd2f06613de0795356f4bb85800000000000000000000000000000000000000000000000000000000000000600000000000000000000000005eae4c2eb8049c8f8f1d35c8164c20684ee2933600000000000000000000000000000000000000000000000000000000000000056170706c65000000000000000000000000000000000000000000000000000000', topics=[0xa2d66134357ca61a7cfe28b780322e6451179110b2348db711cc2ce737c1e6b7]}]
场景2 读取指定地址的所有/最新的事件
我们一般只需要监听使用的合约地址触发的事件,监听参数中填写合约地址,事件消息简化了许多。
public void callAllEvents() throws JniException {
// 参数设置
EventSubParams params = new EventSubParams();
// 全部Event fromBlock设置为1
params.setFromBlock(BigInteger.valueOf(1));
// toBlock默认是-1,表示"latest",处理至最新区块继续等待新的区块
//params.setToBlock(BigInteger.valueOf(300));
// addresses默认为空数组,匹配所有的合约地址
params.addAddress("0x4035a74c5f0daead4ae48a9be0415cfaa8a69d34");
// topics默认为空数组,匹配所有的Event
//params.addTopic(0, "0x2e601d02b417e3eebb1bf0305fed4a7196340cae7656e10ec6c8c1c16eb69f6c");
//params.addTopic(0, "0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d");
// 定义事件回调函数
EventSubCallback callback = new SubscribeCallback();
// 注册事件
EventSubscribe eventSubscribe = EventSubscribe.build(client);
String registerId = eventSubscribe.subscribeEvent(params, callback);
System.out.println("registerId="+registerId);
//停止监听事件
eventSubscribe.unsubscribeEvent( registerId );
}
//回调类
class SubscribeCallback implements EventSubCallback {
SubscribeCallback() {
}
@Override public void onReceiveLog(String eventSubId, int status, List<EventLog> logs) {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
}
}
运行结果只返回了一条记录:
registerId=81916727704349a2ab11f1b2d39ba936
event sub id: 81916727704349a2ab11f1b2d39ba936
status: 0
logs: null
event sub id: 81916727704349a2ab11f1b2d39ba936
event sub id: 81916727704349a2ab11f1b2d39ba936
status: 0
status: 0
logs: [EventLog{logIndex='0', transactionIndex='0', transactionHash='0x65f14efc743b227b131da2b881d751f77a6b644baafce145b0255692d132e86f', blockNumber='307', address='4035a74c5f0daead4ae48a9be0415cfaa8a69d34', data='0x000000000000000000000000a0b0d1cd6a783c0cd2f06613de0795356f4bb85800000000000000000000000000000000000000000000000000000000000000600000000000000000000000005eae4c2eb8049c8f8f1d35c8164c20684ee2933600000000000000000000000000000000000000000000000000000000000000056170706c65000000000000000000000000000000000000000000000000000000', topics=[0xa2d66134357ca61a7cfe28b780322e6451179110b2348db711cc2ce737c1e6b7]}]
logs: [EventLog{logIndex='0', transactionIndex='0', transactionHash='0xc5ebcba146269a0f0e133e33e2bf9935be595efb0a96eb7531c39a61307a3240', blockNumber='308', address='4035a74c5f0daead4ae48a9be0415cfaa8a69d34', data='0x000000000000000000000000a0b0d1cd6a783c0cd2f06613de0795356f4bb8580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000e8aed5848f12dee58599df4f95e7a584ca895d7500000000000000000000000000000000000000000000000000000000000000056170706c65000000000000000000000000000000000000000000000000000000', topics=[0xa2d66134357ca61a7cfe28b780322e6451179110b2348db711cc2ce737c1e6b7]}]
场景3 读取指定地址的某些事件
用于监听特定事件,一般用于立即通知用户或客户端,例如充值、NFT转移、到账。
代码填写topic即可。
场景4 从最新区块开始持续监听事件
fromBlock、toBlock都保持默认值BigInteger.valueOf(-1)即可,不用设置。
会收到到最新区块中的事件消息。
4 启动停止
EventSubscribe接口类中存在 start()、stop()、subscribeEvent()、unsubscribeEvent()方法。我没有深入验证代码,认为层次关系如下, start和stop控制的是注册接口, subscribeEvent()、unsubscribeEvent()控制的是某一类监听。
start()
subscribeEvent(1) 监听事件1
subscribeEvent(2) 监听事件2
......
unsubscribeEvent(1) 取消监听1
unsubscribeEvent(2) 取消监听2
stop()