【问题标题】:Firebase callbacks work during testing but not during runtimeFirebase 回调在测试期间有效,但在运行时无效
【发布时间】:2017-11-02 15:39:33
【问题描述】:

我在 Android 应用中使用 Firebase。该应用程序设置了类似群聊的东西,并允许用户加入。 用户获得一个密钥,然后可以将自己连接到相应的 DatabaseReference。 我们需要检查密钥是否有效。因此,在创建组时,主机会自动将自己添加到用户列表中。然后所有新客户端都可以检查列表中是否有条目。如果列表为空,则密钥无效。

这意味着我需要等待 setValue 调用完成。 Firebase 有很多回调可以告诉我这一点,但它们很成问题。有时,他们根本没有被调用。 我已经在这里问过一个关于这种非确定性行为的问题:How to listen for Firebase setValue completion

现在我发现了这些回调的一个新问题。 我已将我的基础架构更改为异步设置。所有交互都打包到 Callables 中并提交给 ExecutorService。结果是一个 Future。现在,如果我想等待某事完成,我可以等待那个未来。在 Future 内部,我仍然需要使用 Firebase 回调。

代码位于名为 DBConnection 的包装类中。

这是我创建新组(派对)的代码:

public Future<DBState> createParty() {
    // assert entries
    assertState(DBState.SignedIn);

    // process for state transition
    Callable<DBState> creationProcess = new Callable<DBState>() {
        @Override
        public DBState call() throws Exception {

            lock.lock();
            try {
                // create a new party
                ourPartyDatabaseReference = partiesDatabaseReference.push();
                usersDatabaseReference = ourPartyDatabaseReference.child("users");

                // try every remedy for the missing callbacks
                firebaseDatabase.goOnline();
                ourPartyDatabaseReference.keepSynced(true);
                usersDatabaseReference.keepSynced(true);

                // push a value to the users database
                // this way the database reference is actually created
                // and new users can search for existing users when they connect

                // we can only continue after that task has been completed
                // add listeners for success and failure and wait for their completion

                // TODO: we need information that this task has been finished
                // but no callback seems to work
                // onSuccess, onCompletion on the task are not reliable
                // and the child and value event listeners on the userDatabaseReference are not reliable, too
                final CountDownLatch waiter = new CountDownLatch(1);
                usersDatabaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        waiter.countDown();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {
                        waiter.countDown();
                    }
                });
                Task addingTask = usersDatabaseReference.child(user.getUid()).setValue(true);
                addingTask.addOnSuccessListener(new OnSuccessListener() {
                    @Override
                    public void onSuccess(Object o) {
                        waiter.countDown();
                    }
                });
                addingTask.addOnCompleteListener(new OnCompleteListener() {
                    @Override
                    public void onComplete(@NonNull Task task) {
                        waiter.countDown();
                    }
                });
                try {
                    waiter.await();
                } catch (InterruptedException ex) {
                }
                connectToParty();
            } finally {
                lock.unlock();
            }

            // if we could connect, we are now DBState.Connected,
            // otherwise we are still DBState.SignedIn
            return state;
        }
    };

    // start process
    return executorService.submit(creationProcess);

}

你可以这样使用它:

    Future<DBState> creationFuture = dbConnection.createParty();

    try {
        creationFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
    } catch (InterruptedException ex) {
        throw new AssertionError("there should be no interrupt");
    }catch (TimeoutException ex) {
        throw new AssertionError("timeout in party creation");
    }catch (ExecutionException ex) {
        throw new AssertionError("concurrent execution exception");
    }

我已经为此编写了测试。 在测试中,一切正常。我已经执行了至少十几次canCreateParty 测试。

为了确保回调起作用,我将 CountDownLatch 增加到 3 个计数,并向 countDowns 添加断点。到达每个countDown

但在运行时,不会调用任何回调。 没有一个断点到达,等待未来最终超时。

最奇怪的部分是:我在模拟器旁边打开了 Firebase 控制台。我可以看到如何创建新方和添加用户。无论是在测试中还是在运行时,派对创建都按预期工作,并添加了一个新用户。 为什么我在运行时没有收到回调?

【问题讨论】:

  • 只是预感 - “运行时”不涉及不同的密钥库,对吗?就像调试/发布一样 - 因为您将为此使用不同的 SHA1,因此据我所知需要不同的 firebase 密钥
  • 嗯,我不知道。我已将我的调试密钥从 android signingReport 添加到 Firebase。我刚刚检查过:该报告只生成一个密钥。我还有其他地方需要检查钥匙吗?无论如何,如果密钥被拒绝,我无法在运行时写入数据库,或者我可以吗?我可以看到正在创建的新方和正在添加的用户——所有这些都是在运行时进行的。只是缺少完成的回调
  • 嗯,我不确定——我对你在做什么并不完全熟悉,我只真正使用过 FCM。如果签名报告说有一个 SHA,那么这不应该是问题。在您找出问题所在之前,这是毫无意义的问题之一。仔细检查您的测试是否以任何方式作弊,并确保您不只是丢弃来自 firebase 的任何传入消息。您是否从实际设备中获得相同的行为?我知道这也不重要,但仍然
  • @SaikCaskey,是的,这个问题只有在我发现后才有意义。

标签: java android firebase callback firebase-authentication


【解决方案1】:

原因是 Firebase 总是从主线程调用它的回调。

我的测试中的“主”线程被称为“junittestrunnerXXX”。 Firebase 会创建一个名为“main”的新线程来调用回调。

在运行时,“主”线程是实际的“主”线程。如果我对此调用 get() ,它将被永久阻止。 Firebase 检查该线程是否存在,因为它已经存在并且由于它被阻塞,所以什么都没有发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    相关资源
    最近更新 更多