等到firebase检索数据

时间:2015-06-05 06:16:44

标签: java android firebase listener

我想构建一个在child中返回FireBase值的方法。我试着做这样的事情:

public String getMessage(){

    root.child("MessagesOnLaunch").child("Message").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            message = (String) dataSnapshot.getValue();
            Log.i("4r398", "work");
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            Log.e("error", firebaseError.getMessage());
        }
    });
    return message;
}

问题是该方法返回null,这可能是因为该方法不会等到侦听器完成并返回null,因为它的默认值为message。 如何使此方法等到侦听器出现然后返回值。

11 个答案:

答案 0 :(得分:9)

制作界面

 public interface OnGetDataListener {
    //this is for callbacks
    void onSuccess(DataSnapshot dataSnapshot);
    void onStart();
    void onFailure();
}

声明以下函数readData()

public void readData(Firebase ref, final OnGetDataListener listener) {
    listener.onStart();
    ref.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            listener.onSuccess(dataSnapshot);
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            listener.onFailure();
        }
    });

}

调用readData()函数,如下所示

readData(root.child("MessagesOnLaunch").child("Message"), new OnGetDataListener() {
                @Override
                public void onSuccess(DataSnapshot dataSnapshot) {

               //got data from database....now you can use the retrieved data


                }
                @Override
                public void onStart() {
                    //when starting
                    Log.d("ONSTART", "Started");
                }

                @Override
                public void onFailure() {
                    Log.d("onFailure", "Failed");
                }
            });

readData()可以在你的getMessage()方法中调用,甚至可以在o​​nCreate()里面调用

答案 1 :(得分:4)

您可以使用CountDownLatch。 这就是你如何使用它。

public String getMessage(){
   CountDownLatch done = new CountDownLatch(1);
   final String message[] = {null}; 
   root.child("MessagesOnLaunch").child("Message").addListenerForSingleValueEvent(new ValueEventListener() {

    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        message[0] = (String) dataSnapshot.getValue();
        System.out.println("worked");
        done.countDown();
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
        System.out.println("failed"+firebaseError.getMessage());
    }
});

try {
    done.await(); //it will wait till the response is received from firebase.
} catch(InterruptedException e) {
    e.printStackTrace();
}
return message[0];
}

答案 2 :(得分:3)

不要将Firebase用作返回值的函数 - 它违背了它的异步性质。

规划代码结构,允许Firebase执行它的任务,然后在闭包(块)内转到下一步。

在您的代码中,例如,将函数更改为不返回任何内容并在onDataChange中,因为最后一行调用下一个更新UI的函数。

答案 3 :(得分:1)

正如@ CMikeB1在处理servlet和服务其余部分(我们使用同步调用来执行操作)时对java世界中的另一个响应进行评论时,在sdk firebase admin中同时使用这两个选项将很有用,以便开发人员可以选择根据他的情况使用。

我的实现是从等待使用ApiFuture读取同步数据开始的,ApiFuture是firebase管理员的sdk采用的当前接口。这里是a link

public DataSnapshot getMessage() {
    final SettableApiFuture<DataSnapshot> future = SettableApiFuture.create();
    databaseReference.child("MessagesOnLaunch").child("Message").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            future.set(dataSnapshot);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            future.setException(databaseError.toException());
        }
    });
    try {
        return future.get();
    } catch(InterruptedException | ExecutionException e) {
        e.printStackTrace();
        return null;
    }
}

答案 4 :(得分:1)

您可以点击此链接https://youtu.be/LVaIArVY52U

Firebase数据库在检索数据时非常慢,因此可以使应用程序等待过程完成,您可以按照以下步骤

创建一个新的类Ex。 Firebasedata.java并使用Application对其进行扩展,并将此代码添加到OnCreate部分中

public class Firebasedata extends Application {
    @Override
    public void onCreate() {
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        super.onCreate();
    }
}

在清单中声明Java类

 <application
        android:name=".Firebasedata"  <------
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
     ...
     ...
    </application>

然后返回您要获取数据的类,并使用此代码创建一个对话框

    ProgressDialog TempDialog;
    CountDownTimer mCountDownTimer;
    int i=0;

        TempDialog = new ProgressDialog(MainActivity.this);
        TempDialog.setMessage("Please wait...");
        TempDialog.setCancelable(false);
        TempDialog.setProgress(i);
        TempDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        TempDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));

 TempDialog.show();
        mCountDownTimer = new CountDownTimer(2000, 1000)
        {
            public void onTick(long millisUntilFinished)
            {
                TempDialog.setMessage("Please wait..");
            }

            public void onFinish()
            {
                TempDialog.dismiss();
                //Your action like intents are placed here

            }
        }.start();

您可以查看上面的链接,以获得更好的主意

答案 5 :(得分:1)

@Joseph 的最高答案几乎是正确的!他的回答绝对是最简单、最容易理解和实施的,所以我要借助他的回答并纠正一个致命的缺陷。

第 1 步:就像他上面所说的,使用简单的回调接口就可以了,就像这样:

public interface OnGetDataListener {
    //this is for callbacks
    void onSuccess(String dataSnapshotValue);
}

第 2 步:创建一个方法,该方法接受接口的实现作为参数,并查询 Firebase 数据库。 这是原始回答者犯错的地方...而不是传递 DataSnapshot到回调,你必须传递快照的值。

public void readData(Firebase ref, final OnGetDataListener listener) {

    ref.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            // This is how you use the value once it is loaded! Make sure to pass the
            // value of the DataSnapshot object, not the object itself (this was the
            // original answerer's mistake!
            listener.onSuccess(dataSnapshot.getValue());
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {}
    });
}

第3步:调用上面的函数,并传递一个回调接口的实现,在数据加载后处理数据。

readData(root.child("MessagesOnLaunch").child("Message"), new OnGetDataListener() {
                @Override
                public void onSuccess(String dataSnapshotValue) {

                // This is where you can handle the snapshot's value! (log it, add it 
                // to a list, etc.)

                }
            });

答案 6 :(得分:0)

我将在此处保留此内容,以帮助遇到与我相同的问题的任何人。 @Tirupati Singh代码看起来不错,但是我在他的下面写了我的答案,为什么它对我不起作用。使用原子类型有效-

public AtomicInteger getMessage(){
        final AtomicBoolean done = new AtomicBoolean(false);
        final AtomicInteger message1 = new AtomicInteger(0);

        //assuming you have already called firebase initialization code for admin sdk, android etc
       DatabaseReference root = FirebaseDatabase.getInstance().getReference("server");
       root.child("MessagesOnLaunch").child("Message").addListenerForSingleValueEvent(new ValueEventListener() {

        public void onDataChange(DataSnapshot dataSnapshot) {
            message1.set((Integer) dataSnapshot.getValue()); 
            done.set(true);
        }

        public void onCancelled(DatabaseError error) {
            // TODO Auto-generated method stub

        }
    });
    while (!done.get());

    return message1;
}

请注意,该函数只能返回消息类型Integer。我无法让它适用于String,因为String没有原子类型。希望这会有所帮助!

答案 7 :(得分:0)

很抱歉,这次聚会来得有点晚,但是您可以设置观察者/视图模型,而不是让Firebase返回一个值,那么当您运行异步firebase函数时,将以此调用onData方法,您将更新视图对数据建模,然后触发其onDataChanged方法,然后您可以从中更新布局

答案 8 :(得分:0)

Firebase通常需要大约1-2秒来加载数据。

因此请记住,您可以使用Handler添加新线程,使系统等待2秒,然后使用从数据库中检索到的值。

然后,您可以向用户显示“正在加载”对话框,一旦检索到数据,即可关闭该对话框。

 Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                {
                    loadingDialog.dismiss();//dismiss the dialog box once data is retreived
                    Toast.makeText(Act_NewHome.this, "Not enough players online right now!", Toast.LENGTH_SHORT).show();
tvUserName.setText(u_name);
                }
            }
        }, 2000);// 2000 milliseconds = 2seconds

答案 9 :(得分:0)

将此代码添加到onDataChange()方法中

do{
    message = (String) dataSnapshot.getValue();
    if(message.equals(null)) 
        break;
}while (!message.equals(null));

                 

答案 10 :(得分:0)

经过长时间的研究,我找不到它,所以我按自己的方式做了。可以这样做。但是这段代码在主线程上并不安全,因为它等待直到数据恢复。所以如果你在 Main Thread 上使用它,它会有很大的负载。我添加了 WorkerThread Annotation 来告诉它不应该在 UIThread 中使用,另一个 new Thread 是安全的。我测试过,它在 new Thread 上工作,但不在 Main Thread

课堂

public static class DBUtils{
    private boolean fetched;
    private DataSnapshot result;
    private DatabaseReference dbr;
    private DatabaseException err;
    
    private DBUtils(DatabaseReference dbr){
        this.dbr=dbr;
    }
    
    private DataSnapshot fetch(boolean error){
        result=null;
        if(dbr!=null){
            fetched=false;
            dbr.addListenerForSingleValueEvent(new ValueEventListener(){

                    @Override
                    public void onDataChange(DataSnapshot data)
                    {
                        result=data;
                        done();
                    }

                    @Override
                    public void onCancelled(DatabaseError e)
                    {
                        err=e.toException();
                        done();
                    }

                    private void done()
                    {
                        fetched=true;
                        //dbr.removeEventListener(this);
                    }
                });
        }else{
            fetched=true;
        }
        while(!fetched){
            // wait
        }
        if(error&&err!=null){
            throw err;
        }
        return result;
    }
    
    @WorkerThread
    public static @Nullable DataSnapshot getRealtimeValue(@NonNull DatabaseReference ref)
    {
        return getRealtimeValue(ref,false);
    }
    
    @WorkerThread
    public static @Nullable DataSnapshot getRealtimeValue(@NonNull DatabaseReference ref, boolean throwError) throws DatabaseException
    {
        return new DBUtils(ref).fetch(throwError);
    }
}

使用

...
DatabaseReference ref = ...: // Your ref goes here
boolean throwError = false; // if true, the code will throw the error occurred during data fetching, else if false, it will just skip it and return null
DataSnapshot result = DBUtils.getRealtimeValue(ref, throwError);
...

不要忘记给我投票,因为我几乎不需要投票来帮助他人。如果这是您想要的,也将其标记为已解决

——DiLDoST

相关问题