2017-04-11 15 views
3

我正在为我正在处理的Firebase数据库中的“graylist”中的条目编写一个静态方法,并检查用户输入以查看是否该条目存在。我能够很好地找到条目,但由于线程在从Firebase读取/写入数据时的工作方式存在一些限制,因此我无法弄清楚如何通知用户操作是否成功或不。通知用户Firebase搜索是否成功

我有以下方法:

public static void addUser() { 
    final boolean [] complete = new boolean[1]; 
    final String email = Tools.getEmail(); 
    DatabaseReference grayRef = FirebaseDatabase.getInstance().getReference().child("graylist"); 

    // Search the graylist for the email specified. 
    CountDownLatch latch = new CountDownLatch(1); // need this so Parse-Ly doesn't close before event fires 
    grayRef.addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onCancelled(DatabaseError dbe) {} 

     @Override 
     public void onDataChange(DataSnapshot snap) { 
      Iterable<DataSnapshot> graylist = snap.getChildren(); 
      for(DataSnapshot gray : graylist) { 
       String uid = gray.getKey(); 
       String em = gray.getValue(String.class); 
       if(em.equals(email)) { 
        // We found the one we're looking for. Insert its UID into the whitelist. 
        DatabaseReference whiteRef = FirebaseDatabase.getInstance().getReference().child("whitelist"); 
        whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() { 
         @Override 
         public void onComplete(DatabaseError dbe, DatabaseReference dbr) { 
          complete[0] = true; 
          latch.countDown(); 
          if(dbe != null) { 
           System.err.println("Error adding user to whitelist!"); 
           dbe.toException().printStackTrace(); 
          } 
         } 
        }); 
        break; 
       } 
      } 
      if(latch.getCount() > 0) { 
       complete[0] = false; 
       latch.countDown(); 
      } 
     } 
    }); 

    try { 
     latch.await(); // wait for event to fire before we move on 
    } catch(InterruptedException ie) { 
     System.err.println("ERROR: Add user latch interrupted!"); 
     ie.printStackTrace(); 
    } 

    if(complete[0]) ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!"); 
    else ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!"); 
} 

我知道最后的布尔数组如何哈克是,但即使这是不是为我工作。这个想法是使用CountDownLatch,并且只在写入成功时才倒计数。如果读取了该列表中的所有条目并且CountDownLatch仍然为1,则没有任何内容被写入,并且它应该倒数退出该线程,但布尔值将确定该操作是否成功。

问题在于,它在调用onComplete方法之前似乎到达了for循环之后的if块,导致它认为操作失败(即使Firebase上的数据库显示其他情况),并输出了错误的警报。

任何人都可以想到一个更好的方法来处理这个问题,以便我可以正确告知他们,如果用户被添加到白名单或者如果用户没有找到?

回答

0

Firebase数据库上的操作本质上是异步的,因此即使您以上述方式使上述代码正常工作,您最终仍会遇到更复杂的情况,其中大部分代码行正在进行同步而不是处理你的申请。这会导致难以维护的代码很容易被破坏。

因此,处理数据库请求的最简单和“自然”的方式是在代码发生时处理每个事件。这意味着,通知成功更新的用户调用onComplete方法时,并告知出现故障的用户时,所有的孩子实体都被检查,没有发现匹配:

public static void addUser() { 
    final String email = Tools.getEmail(); 
    DatabaseReference grayRef = FirebaseDatabase.getInstance().getReference().child("graylist"); 

    // Search the graylist for the email specified. 
    grayRef.addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onCancelled(DatabaseError dbe) {} 

     @Override 
     public void onDataChange(DataSnapshot snap) { 
      boolean found = false; 
      Iterable<DataSnapshot> graylist = snap.getChildren(); 
      for(DataSnapshot gray : graylist) { 
       String uid = gray.getKey(); 
       String em = gray.getValue(String.class); 
       if(em.equals(email)) { 
        found = true; 
        // We found the one we're looking for. Insert its UID into the whitelist. 
        DatabaseReference whiteRef = FirebaseDatabase.getInstance().getReference().child("whitelist"); 
        whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() { 
         @Override 
         public void onComplete(DatabaseError dbe, DatabaseReference dbr) { 
          complete[0] = true; 
          if(dbe != null) { 
           System.err.println("Error adding user to whitelist!"); 
           dbe.toException().printStackTrace(); 
          } else { 
           Platform.runLater(() -> {         
            ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!"); 
           }); 
          } 
         } 
        }); 
        break; 
       } 
      } 
      if(!found) { 
       Platform.runLater(() -> { 
        ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!"); 
       }); 
      } 
     } 
    }); 
} 
+0

不可能,因为onComplete在单独的线程上,JavaFX不允许你在主线程之外创建对话消息。这很令人沮丧。 –

+0

这篇文章的帮助:http://stackoverflow.com/questions/19755031/how-javafx-application-thread-works? AFAIK Firebase会在主线程上触发'onComplete'回调,因此您需要在JavaFX线程上调用UI。 – manouti

+0

顺便说一句你确定在另一个线程上调用了'onComplete'吗?在Android中,它在主线程上被调用,也许在JVM上它是相同的。在所有情况下,您都可以尝试链接帖子中的机制('Platform.runLater')。 – manouti

0

我没有足够的声誉,所以我不能直接评论你的问题。对于那个很抱歉。

Firebase实际上有自己的线程。在调用grayRef.addListenerForSingleValueEvent后,Firebase会打开一个新线程(或者仅使用其已存在的独立线程),因此您的原始线程会立即继续到if模块,因为它认为要完成该操作,这就是您看到的结果。

因此,您需要的实际上是一旦Firebase完成其操作后与您的主要活动进行通信的侦听器。既然你说你没有使用android,你可以创建一个特定的类来处理Firebase,它还包括接口和监听器。例如,

public class FirebaseHandler { 

    // parameters 
    private DatabaseReference databaseReference; 

    // constructor 
    public FirebaseHandler() { 
     // you'll most probably need these for some actions 
     databaseReference = FirebaseDatabase.getInstance().getReference(); 
    } 

    // interface 
    public interface FirebaseInterface { 
     void firebaseComplete(String serverStatus, String email); 
    } 

    // listener 
    private FirebaseInterface listener; 

    // set listener 
    public void setListener(FirebaseInterface listener) { 
     this.listener = listener; 
    } 

    /* getter for references, these will be useful to avoid calling the wrong place */ 
    // --/
    public DatabaseReference getRoot() { 
     return databaseReference; 
    } 

    // -- /email  /* specify the data structure here */ 
    public DatabaseReference getEmailRef() { 
     return getRoot().child("email"); 
    } 

    // finally your method 
    public static void addUser() { 
     // your codes here... 
     whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() { 
      @Override 
      public void onComplete(DatabaseError dbe, DatabaseReference dbr) { 
       // some action here.... 
       if (listener != null) listener.firebaseComplete("OK", email); 
      } 
     }); 
    } 
} 

你只需要连接监听器在你的活动并执行其方法。所以在你的UI线程中...

class Main implements FirebaseHandler.FirebaseInterface { 

    FirebaseHandler firebaseHandler = new FirebaseHandler(); 
    firebaseHandler.setListener(this); 
    firebaseHandler.addUser(); 

    @Override 
    public void firebaseComplete(String serverStatus, String email) { 
     firebaseHandler.setListener(null);  // optional to remove listener 
     if (serverStatus.equals("OK") { 
      ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!"); 
     } else { 
      ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!"); 
     } 
    } 
} 
+0

如果它在我的原始文章中不明确,这不是一个Android应用程序。这是纯粹的Java。 –

+0

然后第二种方法与接口和侦听器仍然适用。 – tingyik90

相关问题