web-dev-qa-db-ja.com

Firebaseを使用したチャットアプリ:新しいメッセージを受信したときに通知を受け取る-Android

Firebase Realtime Databaseを使用してチャットアプリを開発しています。メッセージを適切に送受信できました。次に、新しいメッセージが受信されるたびに通知を実装します。そのために、Serviceを使用してデータベースの変更をリッスンし、通知を作成するChildEventListenerを作成しました。問題は、onChildAddedメソッドで通知を作成していることです。このメソッドは、データベース内の既存のノードと新しいノードの両方で起動します。これにより、ユーザーがアプリを前後に移動するたびに、同じメッセージに対して通知が複数回作成されます。

実装方法は次のとおりです。

chatMsgsRef.orderByChild(FirebaseDBKeys.LOCATION_LAST_UPDATED).addChildEventListener(new ChildEventListener() {

            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {

                ChatMessage message = dataSnapshot.getValue(ChatMessage.class);

                if (!message.getSenderId().equals(currentUserId)) {

                    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

                    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(NotificationsService.this)
                            .setSmallIcon(R.drawable.message)
                            .setContentTitle("New Message from " + message.getReceipientName())
                            .setContentText(message.getMessage())
                            .setOnlyAlertOnce(true)
                            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
                    mBuilder.setAutoCancel(true);
                    mBuilder.setLocalOnly(false);


                    mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());

                }
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

Whatsappなどの他のチャットアプリケーションで機能するように通知を実装するにはどうすればよいですか?

16
Vaibhav Agarwal

これを行う正しい方法は、新しいチャットがクライアントに送信されたときに、クライアントにプッシュデータメッセージを送信することです。

これ ブログ投稿 はこれを達成する方法を説明しています。基本的に、チャットfirebase refをリッスンし、更新されたときにプッシュ通知を送信するようにサーバーを設定する必要があります。これにより、クライアントはアプリ内またはアプリ外にいて、プッシュを取得できます。

サービスを使用する場合、いくつかの潜在的な問題があります。

まず第一に、あなたは電話を起こしておく必要があります。これにより、バッテリーが消耗します。

次に、Androidはバックグラウンドサービスをいつでも強制終了する可能性があるため、アプリが突然動作しなくなる可能性があります。

第三に、DozeモードではAndroidはネットワークアクティビティをブロックし、アプリのバックグラウンドでの実行を停止します。

12
Shmuel

より良い答えが私に起こった:

この場合に必要なのは、必ずしも新しいメッセージを知ることではありません。未読メッセージを知ることです。

ChatMessageオブジェクトに「読み取り」フラグを設定します(または逆に、最新の読み取りメッセージのタイムスタンプまたはIDを提供する値をどこかに設定します)。

OnChildAddedが起動されるたびに、read == falseかどうかを確認してください。その場合、メッセージが未読であるという通知を表示します(存在する場合は通知を更新することを忘れないでください。そのため、通知は1つだけ表示され、最新の通知が表示されます。読み取りに変更されます。)

ユーザーが複数のデバイスでアプリを使用している場合、読み取り状態を正しくチェックします。電話で最新のメッセージを読んでからタブレットに移動した場合、その新しいメッセージの通知は表示されません。

必要に応じて、この機能を使用して、受信者がメッセージを読んだことを示すこともできます。

それがいつ読まれるかをどのように知っていますか?おそらく、画面に追加するだけです。おそらく、それが表示されていること(画面からスクロールされていないこと)を確認し、数秒間表示されるようにします。

5
Chad Schultz

AddValueEventListenerを試しましたか?

https://www.firebase.com/docs/Android/guide/retrieving-data.html#section-start

// Get a reference to our posts
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");
// Attach an listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        System.out.println(snapshot.getValue());
    }
    @Override
    public void onCancelled(FirebaseError firebaseError) {
        System.out.println("The read failed: " + firebaseError.getMessage());
    }
});

「このメソッドは、Firebaseリファレンスに新しいデータが追加されるたびに呼び出されます。これを実現するために余分なコードを記述する必要はありません。」

編集:「初期データで一度トリガーされ、データが変更されるたびにトリガーされます。」試したことはありませんが、メソッドが最初に起動されたときにフラグを設定すると思います。その後、メソッドとそのフラグが設定されるたびに(つまり、2回目、3回目、4回目など)、スナップショットから最新のオブジェクトを取得します。

3
Chad Schultz

@Chad Shultzの回答と元のコードを組み合わせたもので、データベース構造がいくつか追加されています。

最初に意図したように、クエリを使用します

chatMsgsRef.orderByChild(...)

ただし、次のレイアウトになるようにデータ構造を変更します

>root
    > users
        > userID_001
            > chats
                > chat_001:true
        > userID_002
            > chats
                > chat_001:true
    > chats
        > chat_001
            > participants
                > userID_001:true
                > userID_002:true
            > messages
                > msg_001
                    > sender:"userID_001"
                    > text:"some message"
                    > time:"some time"
    > message_read_states
        > chat_001
            > msg_001
                > userID_001:true
                > userID_002:false

したがって、メッセージが送信されるたびに、「メッセージ」ノードにプッシュされます。次に、システムはすべての「参加者」を取得し、そのチャット/メッセージの「message_read_states」ノードの下にプッシュします。送信者を除く全員の値はfalseです。誰かがメッセージを読むと、そこで値をtrueに変更します。

ここで、サービスはユーザーに通知するメッセージを決定する必要があります。サービスは、リスナをmessage_read_states/chat_Xに配置し、子フィールド「userID_X」の値(現在のユーザーが誰であるかに応じて)でそれを並べ替えます。クエリは、チャットごとにメッセージIDのみを返すようにします。メッセージIDの下には値「userID_X:false」があります

したがって、この例では、「userID_002」の場合、クエリは「msg_001」を返しますが、「userID_001」の場合、そのキーに必要な値が見つからないため(送信者であるため)、何も返しません。

クエリは、サービスのonStartCommand内で次のように構築されます。

    //TODO: Get the ID of the chat the user is taking part in
    String chatID = "chat_001";

    // Check for new messages
    FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
    if (currentUser != null){
        String UID = currentUser.getUid();
        DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
        Query query = rootRef.child("message_read_states").child(chatID).orderByChild(UID).equalTo(false);
        query.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                String messageID = dataSnapshot.getKey();
                //TODO: Handle Notification here, using the messageID
                // A datasnapshot received here will be a new message that the user has not read
                // If you want to display data about the message or chat,
                // Use the chatID and/or messageID and declare a new 
                // SingleValueEventListener here, and add it to the chat/message DatabaseReference.
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                String messageID = dataSnapshot.getKey();
                //TODO: Remove the notification
                // If the user reads the message in the app, before checking the notification
                // then the notification is no longer relevant, remove it here.
                // In onChildAdded you could use the messageID(s) to keep track of the notifications
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
    }

もちろん、ユーザーが複数のチャットに関与する可能性が非常に高くなります。ユーザーに関連付けられているすべてのチャットを繰り返し処理し、各チャットにクエリを実装する必要があります。

出典:現在、チャットシステムを備えたFirebaseアプリの開発に取り組んでいます

2
gorrox23