wait until firebase retrieves data

2020-02-05 06:39发布

I want to build a method that returns a child value in FireBase. I tried to do something like this:

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;
}

The problem is that the method returns null that is probably because the method doesn't wait until the listener finishes and return null because its the default value of message. How can I make this method wait until the listener occurs and then return the value.

7条回答
仙女界的扛把子
2楼-- · 2020-02-05 06:44

Don't use Firebase as functions that return values - it goes against it's asynchronous nature.

Plan code structure that allows Firebase to perform it's task and then within the closure (block) go to the next step.

In your code, for example, change the function to not return anything and within the onDataChange, as the last line call the next function to update your UI.

查看更多
爷的心禁止访问
3楼-- · 2020-02-05 06:57

Make an interface

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

Declare the following function 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();
        }
    });

}

Call the readData() function as follows

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() can be called inside your getMessage() method or even inside onCreate()

查看更多
家丑人穷心不美
4楼-- · 2020-02-05 07:00

You can follow this link https://youtu.be/LVaIArVY52U

Firebase database is quite slow when retrieving the data so to make the application wait for the process to complete you can follow this

Create a new class Ex. Firebasedata.java and extent it with Application and add put this code in the OnCreate sections

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

declare the java class in the manifests

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

Then go back to the class where you want to get the data and create a dialog with this code

    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();

You can look into the link above and get a better idea

查看更多
霸刀☆藐视天下
5楼-- · 2020-02-05 07:01

Sorry to be a little late to this party but rather than have firebase return a value you could set up an observer / view model, then when you run your async firebase function the onData method is called from this you would update your view models data which then triggers its onDataChanged method which you can then update your layout from

查看更多
【Aperson】
6楼-- · 2020-02-05 07:05

I'll leave this here to help anyone who faces the same problem as me. @Tirupati Singh code looks nice but I wrote my answer below his why it didn't work for me. Using Atomic type worked -

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;
}

Note the function can only return message type Integer. I couldn't get it to work for String as no Atomic type for String. Hope this helps!

查看更多
Luminary・发光体
7楼-- · 2020-02-05 07:08

You can use CountDownLatch. This is how you can use it.

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];
}
查看更多
登录 后发表回答