【问题标题】:Event Listeners listening after being removed移除后监听的事件监听器
【发布时间】:2018-12-09 11:22:10
【问题描述】:

我的班上有很多事件监听器,之前我问过如何在这些监听器发生一次后杀死它们,我已经解决了这个问题。

它在某些情况下有效,但在其他情况下我遇到了问题。例如,我有一个具有事件侦听器的方法。

    private void startGame() {
    HomePage.getCurrentGameID(new HomePage.CallbackID() {
        @Override
        public void onSuccess(final String currentGameID) {
            games.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                    if (dataSnapshot.child(currentGameID).getValue().equals(1)) {
                        getResults();
                        Log.i("FlipCoin", "``startGame() happened");
                    } else {
                        return;
                    }
                    games.removeEventListener(this);
                }
}

我有启动游戏的方法,在onDataChange() 结束时我有games.removeEventListener(this)。但有时,即使在执行此操作后,它仍会运行代码,我知道这一点,因为每次方法发生时它都会记录到 LogCat。

我的代码中只有一个实例调用startGame(),并且该实例在此计时器中

private void runCounter() {
    final Runnable counter = new Runnable() {
        @Override
        public void run() {
                Log.i("FlipCoin", "``runCounter() happened");
                startGame();
            }
        }
    };
    counter.run();
}

我去掉了很多不必要的代码,所以这些方法可能没有太大的实际意义,但它们被用作我的问题的示例。

无论如何,正如您在 runCounter() 方法中看到的那样,它调用 startGame() 就像它应该的那样,并且我也有一个登录方法,让我知道何时调用此 runCoutner() 方法。

奇怪的是 runCounter() 方法只在 logcat 中记录了一次,但它一直在记录 startGame(),即使我只在我的代码中调用它一次,并且我在方法结束。

为什么会这样,我超级糊涂!


我正在使用可运行(与问题无关)

private void runCounter() {
    final TextView headsOrTails = findViewById(R.id.HeadsOrTails);
    final Handler handler = new Handler();
    final AtomicInteger n = new AtomicInteger(3);
    final Runnable counter = new Runnable() {
        @Override
        public void run() {
            headsOrTails.setText("Flipping in: " + Integer.toString(n.get()) + " seconds");
            if (n.getAndDecrement() >= 1)
                handler.postDelayed(this, 1000);
            else {
                headsOrTails.setText("Flipping");
                Log.i("FlipCoin", "``runCounter() happened");
                startGame();
            }
        }
    };
    counter.run();
}

【问题讨论】:

  • 您可能应该在addValueEventListener 之前添加日志,以了解它是否被多次添加。根据这段代码,如果removeEventListener被正确实现,这应该不会发生。
  • 你为什么不使用addListenerForSingleValueEvent,它的全部目的是只听一次?
  • 你在 runCounter 中的 Runnable 也是完全没有必要的。如果你只调用 startGame(),你会得到完全相同的效果。
  • @DougStevenson 将其转换为 addListenerForSingleValueEvent 会出现一些问题,如果您仔细阅读我的帖子,您就会知道我删除了代码来简化问题,Runnable 确实有一个目的。
  • 我很想知道这个 Runnable 有什么实际用途,现在它的行为与根本没有它相比。您通常只将 Runnable 与新线程或某个线程池或执行程序结合使用。而且我也很想知道为什么 addListenerForSingleValueEvent 对你不起作用,因为它是 SDK 的核心功能。正确使用应该没有问题。

标签: java android firebase firebase-realtime-database


【解决方案1】:

你用错了。

如果您只想听一次值而不是删除侦听器,请使用:addListenerForSingleValueEvent

【讨论】:

  • 这意味着您的代码不止一次调用该方法
  • 我的代码中只有一个地方,我已经看过很多次了,那个方法被调用了。并且调用该方法的 Runnable 也只被调用一次。
  • 您在代码中只有一次调用并不意味着代码不会被多次调用......您可以开始组织您的代码,这实际上比您最终会发现错误时非常混乱。您创建了许多 Runnable 并在同一个线程中执行它们......这是没有意义的。创建不必要的匿名类是一种不好的做法。另外,没有解释 new HomePage.CallbackID() 做了什么...您使用 get 方法并将某些内容作为参数传递...这又是基本代码设计
  • 如果进行了所有更改,您仍然无法理解为什么被多次调用,简单调试并查看堆栈跟踪哪个方法正在调用它
  • 就像我在帖子中所说的那样,我已经删除了很多代码,只是为了将问题简化到核心。没有任何看起来毫无意义的代码实际上是毫无意义的。