【问题标题】:Android for loop not looping ? FirebaseAndroid for 循环不循环?火力基地
【发布时间】:2017-07-15 20:49:43
【问题描述】:

我对以下代码有疑问:我不明白为什么我的 for 循环在调用 if (gameExists[0] == false){... 之前不循环。

    Button playButton = (Button) findViewById(R.id.play_button);
    playButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            user = mFirebaseAuth.getCurrentUser();

            final String gameCateg = String.valueOf(categorySelected[0]);

            DatabaseReference allExistingGamesToMatchKeys = mFirebaseDatabase.getReference().child("gamestomatchkeys");
              allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    final boolean[] gameExists = {false};
                    Log.d("gameExists before loop ", String.valueOf(gameExists[0]));

                    for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
                        final String currentKey = postSnapshot.getKey();
                        //check player 2 is not the same as player 1 so that we don't match the same player
                        if(gameCateg.equals(postSnapshot.getValue().toString())){
                            mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
                                @Override
                                public void onDataChange(DataSnapshot dataSnapshot) {
                                    String namePlayer1 = dataSnapshot.getValue(String.class);
                                    if(!(user.getUid().toString().equals(namePlayer1))){
                                        mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
                                        mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
                                    gameExists[0] = true;
                                    Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));


                                    Intent intent = new Intent(getApplicationContext(), Game.class);
                                        intent.putExtra("gameIdKey", currentKey);
                                        startActivity(intent);
                                    }
                                }

                                @Override
                                public void onCancelled(DatabaseError databaseError) {
                                }
                            });
                            break;
                        }
                    }
                    if (gameExists[0] == false){
                        Log.d("gameExists in if", String.valueOf(gameExists[0]));

这是我在日志中得到的,按以下顺序:

gameExists before loop: false

gameExists in if: false

gameExists 在 for 循环中:true

我不明白我为什么会得到

gameExists in if: false

之前

游戏存在于 for 循环中:true

我希望我的循环在if (gameExists[0] == false){... 之前被调用并完全循环,我应该修改什么?

谢谢!

【问题讨论】:

    标签: android for-loop firebase firebase-realtime-database


    【解决方案1】:

    为简单起见,Firebase 数据库请求位于代码流之外。看看这个:

    // here is first point 
    allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // here is second point
        }
        ...
    }
    // here is third point
    

    上面的例子会这样执行

    --> 第一点--> 第三点

    那么第二点在哪里呢?每当 Firebase 从在线数据库获取数据时,都会执行第二点,因此它在流程之外(但大多数情况下,它将在第三点之后执行

    总之,如果您需要在 Firebase 请求完成后执行一些代码,请将其放在 onDataChange()

    您可以look at this 供参考

    【讨论】:

    • 谢谢,我明白它是如何工作的。在 onDataChange 中,我只想在我的所有列表都被循环之后才调用我的“if”。所以我想我需要一个条件来说明这一点,例如 if(这是 DataSnapshot 列表中的最后一个元素)或 if(DataSnapshot 列表中的下一个元素为空)。我尝试了一些东西,例如 if(currentKey == mFirebaseDatabase.getReference().child("gamestomatchkeys").limitToLast(1).toString()) 但我没有得到我想要的。你知道怎么做这样的条件吗?
    • 最简单的方法是创建一个int 值,其中包含来自"gamestomatchkeys" 请求的dataSnapshot 计数,以及一个计数器的另一个int 值。然后在"games" 请求中的onDataChange 中,将counter int 加一。然后在同一 onDataChange 的最后一部分,比较第一个 int 值和计数器。如果匹配,那么你已经到达终点
    【解决方案2】:

    这是因为“gameExists in for loop”实际上不在 for 循环中。它在 for 循环中创建的回调中。

                           mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
                                @Override
                                public void onDataChange(DataSnapshot dataSnapshot) {
                                    String namePlayer1 = dataSnapshot.getValue(String.class);
                                    if(!(user.getUid().toString().equals(namePlayer1))){
                                        mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
                                        mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
                                    gameExists[0] = true;
                                    Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));
    
    
                                    Intent intent = new Intent(getApplicationContext(), Game.class);
                                        intent.putExtra("gameIdKey", currentKey);
                                        startActivity(intent);
                                    }
                                }
    
                                @Override
                                public void onCancelled(DatabaseError databaseError) {
                                }
                            });
    

    在这里,您正在创建一个 ValueEventListener 的新实例,并且您正在覆盖其中的方法。您没有在实例化点执行这些方法中的代码。每当您的 ValueEventListener 决定调用 onDataChange() 方法时,就会调用该代码。

    我不完全确定这个对象是什么,或者它是如何工作的:

    mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1")
    

    但如果我是你,我会先看看我是否可以从中获取 DataSnapshot,然后在你想在 Listener 之外执行的任何代码上使用该快照。

    如果我不得不猜测,可能会有这样的方法:

    mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").getDataSnapshot();
    

    你可以打电话。然后将 onDataChange() 内部的所有代码复制到侦听器外部。

    (我不知道该方法是否退出,但我认为有某种方法可以获取当前的 DataSnapshot)

    【讨论】:

    • 我不确定是否理解:当侦听器附加到我的引用时会调用 onDataChange(),这是在我的 for 循环的左括号和右括号之间完成的。那么为什么不应该“循环”呢? From what I've seen 我需要一个监听器来获取值,没有诸如“.getDataSnapshot()”之类的东西。在这个监听器中,我需要使用从 for 循环中获得的变量“currentKey”。
    • 没有getDataSnapshot()方法,如果要获取价值只能是addListenerForSingleValueEventaddValueEventListener()
    猜你喜欢
    • 2019-09-10
    • 2012-07-14
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-13
    • 1970-01-01
    相关资源
    最近更新 更多