@alexvdem

Как в Firebase Cloud Messaging реализовать push пользователю?

Всем привет. Вчера потратил 3 часа на изучение вопроса, и честно говоря немного в тупике.

Дано: приложение-клиент, разрабатываемое в Android Studio. В приложении реализована авторизация пользователя посредством Google Sign In. Приложение общается с backend на PHP посредством HTTPS POST.

Задача: реализовать push от сервера к пользователю.

Вариант решения: получить токен Firebase и пушить с помощью его сообщение через curl на сервере.
Для этой цели, чтобы получить токен в клиенте реализован вот такой код:
Get Firebase token
// [START log_reg_token]
            FirebaseMessaging.getInstance().getToken()
                    .addOnCompleteListener(new OnCompleteListener<String>() {
                        @Override
                        public void onComplete(@NonNull Task<String> task) {
                            if (!task.isSuccessful()) {
                                Log.w(TAG, "Fetching FCM registration token failed", task.getException());
                                return;
                            }
                            // Get new FCM registration token
                            token = task.getResult();
                            // Log and toast
                            String msg = getString(R.string.msg_token_fmt, token);
                            Log.d(TAG, msg);
                            Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
                        }
                    });
            // [END log_reg_token]

Проблема: все это работает замечательно, сообщения от сервера приходят стабильно, с задержкой 1-2 секунды, и все бы было хорошо, если бы не одно но... сообщения приходят для приложения, а не для пользователя. В общем то это логично, т.к. указанный выше код генерит токен (который адресует получателя в системе Firebase Cloud messaging) для приложения, независимо от того, какой пользователь зашел в приложение. Но мне надо чтобы push генерился и доставлялся (или не доставлялся) именно для конкретного пользователя. Т.е. токен должен быть уникальным не для приложения, а для авторизованного Google Sign In пользователя.
Путем гугления узнал, в Firebase Authentication есть опция для Google Sign In https://firebase.google.com/docs/auth/android/goog...
Вроде бы решение проблемы, но уже другая проблема. Указанный в документе код:
Firebase Authentication for Google Sign In
private void firebaseAuthWithGoogle(String idToken) {
        AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // Sign in success, update UI with the signed-in user's information
                            Log.d(TAG, "signInWithCredential:success");
                            FirebaseUser user = mAuth.getCurrentUser();
                            updateUI(user);
                        } else {
                            // If sign in fails, display a message to the user.
                            Log.w(TAG, "signInWithCredential:failure", task.getException());
                            updateUI(null);
                        }
                    }
                });
    }

у меня крэшится на попытке получить идентификационный токен, путем
Get Firebase User Token
protected void onStart() {
        super.onStart();
        /*Directly start MainActivity if Already LoggedIn*/
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        if(account != null) {
            MainActivity.account = account;
            startActivity(new Intent(this, MainActivity.class));
            finish();
        }
        String firebasetoken  = FirebaseAuthWithGoogle(account.getIdToken());
    }

на строке
String firebasetoken  = FirebaseAuthWithGoogle(account.getIdToken());

Console crash report
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.carnet.messenger, PID: 19027
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.google.android.gms.auth.api.signin.GoogleSignInAccount.getIdToken()' on a null object reference
at com.carnet.messenger.LoginActvity.onStart(LoginActvity.java:67)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
at android.app.Activity.performStart(Activity.java:8024)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
I/Process: Sending signal. PID: 19027 SIG: 9
Disconnected from the target VM, address: 'localhost:35225', transport: 'socket'

хотя в консоли, при входе я вижу, что этот токен выдается...
D/FirebaseAuth: Notifying id token listeners about user ( S9sZO5kuY7EcSvGRKoiVTcvSw0B2 ).

И это тот же самый токен, что я вижу в консоле аутентифицированных пользователей FireBase Authentication.

Вопрос номер раз: как же его получить, этот самый токен чтобы передать на сервер?
Вопрос номер два: как по этому токену делать сообщения push? https://fcm.googleapis.com/fcm/send такие токены пользователей не принимает, хотя с токеном для приложения все работает нормально.
Всем откликнувшимся большое спасибо за мысли.
  • Вопрос задан
  • 59 просмотров
Решения вопроса 1
zolt85
@zolt85
Программист
Надо генерить токен на мобилке, и засылать на сервер, привязывая его к пользователю (класть в базу связку userId, token, например. Для отправки сообщения конкретному пользователю, берем его токены (да их может быть больше чем один, т.к. устройств у пользователя тоже может быть больше чем один) и шлём сообщения с такими токенами.
Получение токена у меня сделано так
FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                Log.w("MainActivity", task.exception)
                return@OnCompleteListener
            }
            val token = task.result?.token ?: return@OnCompleteListener
            async {
                saveToken(token) //тут засылаем токен на сервер и там вяжем к пользователю
            }
         })


Отправка сообщения с сервера выглядит так
val tokens = messageTokenService.allUserTokens(userId) // берем все токены из БД для пользователя
    val data = ObjectMapper().writeValueAsString(msgData) // это собссно сообщение которое хотим отправить
    log.info("message size in bytes: ${data.toByteArray().size}")
    tokens.forEach { token ->
        val m = Message.builder()
                        .putData(msgDataProperty, data)
                        .putData("type", messageType.toString())
                        .setToken(token)
                        .build()
            try {
                FirebaseMessaging.getInstance().send(m)
            } catch (e: Exception) {
                log.error(e.message, e)
                messageTokenService.deleteToken(userId, token) // если не удалось по этому токену отправить, дропаем его из базы, чтобы не хранить хлам
            }


Для отправки сообщений с сервера пользую гугловую либу
implementation 'com.google.firebase:firebase-admin:6.8.1' // версии надо смотреть, может уже новее есть


И самое главное: всё это есть в документации по ссылке https://firebase.google.com/docs/cloud-messaging/s...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы