JDA开发Discord加密货币机器人:无法复用对象取消定时任务
问题:Discord加密货币定时推送任务无法取消的解决方案
我正在开发一款集成CoinMarketCap API的Discord机器人,用于查询加密货币价格并返回给用户。我实现了一个命令,触发后会在比特币收盘时间(GMT凌晨12点/巴西时间晚上9点)推送其价格,但尝试取消该定时任务时,价格仍会按时推送。
核心问题:启动命令会创建一个带有独立Timer的ScheduledAlert对象,但执行停止命令时,只能创建新的ScheduledAlert对象,无法访问启动时创建的原对象来取消任务。
相关代码如下:
//Command Handler Class else if (command.equals("bitcoin-scheduled-alert-start")) { ScheduledAlert scheduledAlert = new ScheduledAlert(event.getTextChannel()); scheduledAlert.start(LocalTime.of(21,00, 00)); event.reply("The daily closing price of Bitcoin will be displayed from now on!").queue(); } else if (command.equals("bitcoin-scheduled-alert-stop")) { ScheduledAlert disabledScheduledAlert = new ScheduledAlert(event.getTextChannel()); disabledScheduledAlert.stop(); event.reply("The current scheduled alert has been stopped!").queue(); }
public class ScheduledAlert { //Bitcoin update at every candle close (12 am GMT [9 pm BRT]) private final Timer timer; private TimerTask task; private final TextChannel channel; private final String symbol; public ScheduledAlert(TextChannel channel) { this.channel = channel; this.symbol = "BTC"; this.timer = new Timer(); } public void start(LocalTime time) { //Getting the time from BotCommands parameter // Cancel the task if it is already scheduled (NOT WORKING) if (task != null) { task.cancel(); } task = new TimerTask() { //Start TimerTask @Override public void run() { System.out.println("Started"); CryptoPrice cmcApi = new CryptoPrice(symbol); //Get Bitcoin price double price = cmcApi.getPrice(symbol); NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.US); String priceString = formatter.format(price); System.out.println((priceString)); channel.sendMessage("The closing price of Bitcoin is " + priceString).queue(); } }; ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Sao_Paulo")); //Sync date with time zone ZonedDateTime scheduledTime = ZonedDateTime.of(now.toLocalDate(), time, now.getZone()); if (now.compareTo(scheduledTime) > 0) { scheduledTime = scheduledTime.plusDays(1); //If the command has been triggered after time in LocalTime, set it to next day at the set time } long delay = Duration.between(now, scheduledTime).toMillis(); timer.schedule(task, delay, TimeUnit.DAYS.toMillis(1)); } public void stop() { if (task != null) { task.cancel(); task = null; System.out.println("Stopped"); } }
解决方案
1. 全局存储ScheduledAlert实例
在Command Handler类中维护一个全局映射,关联每个频道对应的定时任务实例,这样启动时存入、停止时取出原实例操作:
// 在Command Handler类中添加全局映射 private Map<TextChannel, ScheduledAlert> alertMap = new HashMap<>(); // 启动命令逻辑 else if (command.equals("bitcoin-scheduled-alert-start")) { TextChannel channel = event.getTextChannel(); // 先清理该频道已有的任务(如果存在) ScheduledAlert existingAlert = alertMap.get(channel); if (existingAlert != null) { existingAlert.stop(); alertMap.remove(channel); } // 创建并启动新任务 ScheduledAlert scheduledAlert = new ScheduledAlert(channel); scheduledAlert.start(LocalTime.of(21, 00, 00)); alertMap.put(channel, scheduledAlert); event.reply("The daily closing price of Bitcoin will be displayed from now on!").queue(); } // 停止命令逻辑 else if (command.equals("bitcoin-scheduled-alert-stop")) { TextChannel channel = event.getTextChannel(); ScheduledAlert existingAlert = alertMap.get(channel); if (existingAlert != null) { existingAlert.stop(); alertMap.remove(channel); event.reply("The current scheduled alert has been stopped!").queue(); } else { event.reply("No active scheduled alert found for this channel!").queue(); } }
2. 优化ScheduledAlert的stop方法
确保彻底释放Timer资源,避免残留线程:
public void stop() { if (task != null) { task.cancel(); task = null; } timer.cancel(); // 取消整个Timer实例,释放资源 System.out.println("Stopped"); }
内容的提问来源于stack exchange,提问作者Yago Andrade
相关产品推荐
相关产品推荐

