如何监听Firebase setValue完成事件

4
我希望在Firebase中设置类似于群聊的东西。如果您有正确的群组ID,就可以加入。
问题在于Firebase引用总是存在的。它们是动态创建的。为了允许在加入过程中进行一些错误检查,我想将创建新数据库条目的用户添加到用户表中。
public void createParty() {

    // you need to be signed in to create a party
    assertState(DBState.SignedIn);

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

    // write our user to the table of users, now the ourPartyReference actually exists
    // and if you look for existing users, you will always find at least the original creator
    Task initTask = usersDatabaseReference.child(user.getUid()).setValue(true);

完成这些步骤后,我调用一个辅助方法connectToParty来设置一些引用并将数据库连接状态更改为DBState.Connected。现在,您可以获取聚会的密钥并将其传输给其他用户。在此,密钥是指Firebase中的名称。
问题在于:其他用户需要检查他们是否使用了有效的密钥进行连接。他们可以通过检查key/users下是否有数据来完成此操作。因此,只有当第一个用户成功写入数据库时,聚会才是有效的。
有时写入过程需要很长时间,有时似乎完全失败。因此,我设置了监听器:
Task initTask = usersDatabaseReference.child(user.getUid()).setValue(true);

initTask.addOnSuccessListener(new OnSuccessListener() {
    @Override
    public void onSuccess(Object o) {
        connectToParty();
    }
});

initTask.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        changeState(DBState.SignedIn, DBTransition.Failure);
    }
});

这是一个测试内容,测试设置了一个连接,登录并创建一个派对。如果在特定的超时时间内创建了该派对,则测试通过。

有9次中的10次这个过程都能正常进行。但有时测试会失败。

这与超时时间太短无关。任务的回调函数根本没有被调用。我读到只有当您的连接在线时,此回调才有效。

因此,在我的代码中添加了 firebaseDatabase.goOnline(); ,就在任务之前。但这并没有帮助。

我通过局域网连接到互联网。所有其他测试都通过,并且它们也需要访问firebase。例如登录过程。

正确的方法是什么,可以将值写入数据库并等待写入完成?


当测试失败时,出现了什么错误? - Frank van Puffelen
这是一个超时。我等待回调10秒钟,然后测试失败。 - lhk
所以两者都没有被称为。 - lhk
如果既没有调用成功侦听器也没有调用失败侦听器,那么这意味着客户端尚未从服务器收到响应。如果您在单元测试中运行此代码,10秒钟似乎相当乐观。尝试将其扩展到1-2分钟以查看是否确实是造成此行为的原因。 - Frank van Puffelen
4个回答

10

对于 Kotlin 用户,您可以使用此代码

firebaeRef.setValue("your_value", { error, ref ->
            //setValue operation is done, you'll get null in errror and ref is the path reference for firebase database
        })

这里,firebaseRef 是指被设置值的节点/子节点的引用,而your_value是要设置的值。


9
正如官方文档所述:

如果您想知道数据何时提交,可以添加完成侦听器。setValue()和updateChildren()都需要一个可选的完成侦听器,当写入已提交到数据库时调用该侦听器。

这是一个简单的示例:
ref.setValue("I'm writing data to a Firebase database", new Firebase.CompletionListener() {
    @Override
    public void onComplete(FirebaseError firebaseError, Firebase firebase) {
        if (firebaseError != null) {
            System.out.println("Data could not be saved. " + firebaseError.getMessage());
        } else {
            System.out.println("Data saved successfully.");
        }
    }
});

1
我已经尝试了这两种方法。问题不再出现了。我已经删除了新代码,问题也不再出现了。调试这种非确定性行为非常困难。 - lhk
1
我理解你。这有点难,但我认为这是实现这个的唯一方式。你认为我的回答有帮助吗? - Alex Mamo
一切都好吗?我能为您提供其他信息的帮助吗? - Alex Mamo
1
嗨,Alex,不幸的是,问题仍未解决。缺失的回调有时仍会发生(极其罕见)。这几乎不可能正确地进行调试。现在我发现了一个新的错误,对我来说是确定性的:https://stackoverflow.com/questions/47079227/firebase-callbacks-work-during-testing-but-not-during-runtime - lhk
你认为我对这个问题的回答有帮助吗? - Alex Mamo
显示剩余6条评论

0
你可以考虑为每个可能的返回结果添加监听器。
  ref.child(user.getUid()).setValue(true)
        .addOnFailureListener {
            print("Update failed: $it") 
        }.addOnCanceledListener {
            print("Update canceled") 
        }.addOnSuccessListener {
            print("Update completed ")
        }

-2

只需将您的代码放在try catch块中,然后在Toast中查看错误即可。

btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText t=findViewById(R.id.edittext);

              String data=t.getText().toString();

                try {
                    ref.setValue(data);
                    Toast.makeText(MainActivity.this, "Inserted", Toast.LENGTH_SHORT).show();
                }catch (Exception e){
                    Toast.makeText(MainActivity.this, ""+e.getMessage(), Toast.LENGTH_SHORT).show();
                }
        
            }
        });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接