web-dev-qa-db-ja.com

firebaseがデータを取得するまで待つ

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が返されるためです。リスナーが発生するまでこのメソッドを待機させ、値を返すにはどうすればよいですか

18
ben

インターフェースを作る

 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()メソッド内、またはonCreate()内でも呼び出すことができます。

9
Joseph Varghese

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];
}
6
Tirupati Singh

値を返す関数としてFirebaseを使用しないでください。非同期の性質に反します。

Firebaseがそのタスクを実行できるようにするコード構造を計画し、クロージャー(ブロック)内で次のステップに進みます。

たとえば、コードで、最後の行が次の関数を呼び出してUIを更新するように、関数を変更して何も返さないようにします。

@ CMikeB1がJavaの世界で別の応答にコメントしているので、サーブレットとサービスの残りを処理しているときは、同期呼び出しを使用して操作を実行するので、sdk firebase adminに両方のオプションがあると便利です。開発者がケースの用途に応じてどちらを使用するかを選択できること。

Firebase管理者のSDKで採用されている現在のインターフェースであるApiFutureを使用して、同期データの読み取りを待機しないように実装しました。 リンク

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;
    }
}
2
Pedro Bacchini

私と同じ問題に直面している人を助けるためにここにこれを残します。 @Tirupati Singhのコードは見栄えがいいのですが、なぜうまくいかなかったのかを彼の回答の下に書きました。 Atomicタイプの使用は機能しました-

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のみを返すことができることに注意してください。文字列の原子型がないため、文字列で動作させることができませんでした。お役に立てれば!

1
Zod

このリンクをたどることができます https://youtu.be/LVaIArVY52

データの取得時にFirebaseデータベースが非常に遅いため、プロセスが完了するまでアプリケーションを待機させるには、次のようにします

新しいクラスを作成します。 Firebasedata.Javaおよびそれをアプリケーションで拡張し、このコードを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();

上記のリンクを調べて、より良いアイデアを得ることができます

1
Gnana Sreekar

このパーティーに少し遅れて申し訳ありませんが、firebaseが値を返すのではなく、オブザーバー/ビューモデルを設定できます。その場合、非同期のfirebase関数を実行すると、onDataメソッドが呼び出され、ビューモデルデータが更新されます。次に、レイアウトを更新できるonDataChangedメソッドをトリガーします

0
martinseal1987