Не приходят данные в функцию onMessageReceived в сервисе по приему push сообщений

У меня стоит задача: при передачи PUSH передать еще данные, скажем ИД чата или ИД клиента. Пробовал первым способом: не помогло: Способ первый: Значение Received data: {} при получении сообщения через fcm - пустое. Я хотел бы получать значение переменной ChatID, в функции onMessageReceived при приеме сообщения. При этом когда свернуто приложение, то title и body отображаются на эмуляторе-приемнике в пуш сообщении. Но не в в функции onMessageReceived. В функции onMessageReceived все пусто, данные пустые. Вот код сервиса:

@file:JvmName("PushNotificationServicelUtil")
package fcmpushnotificationshttpv1

import android.util.Log
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.google.firebase.messaging.ktx.messaging
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.future
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import java.util.Objects
import java.util.concurrent.CompletableFuture


class PushNotificationService: FirebaseMessagingService() {
val TAG: String = "0000"
    override fun onNewToken(token: String) {
        super.onNewToken(token)
        sendTokenToServer(token)
        // Update server
    }

    override fun onMessageReceived(message: RemoteMessage) {

        Log.d(TAG, " onMessageReceived ")
        super.onMessageReceived(message)
       // message.data
        val map: Map<String, String> = message.data
        Log.d(TAG, "Received data: $map")
        val title: String? = map["title"]
        val chatId: String? = map["chatId"]
        Log.d(TAG, " onMessageReceived : $chatId, title: $title")
        // Respond to received messages
    }

    suspend fun getTokenMy(): String {
        val token = Firebase.messaging.token.await()

        return token.toString()
    }






//сохраняет токен в БАЗУ ДАННЫХ
    suspend fun saveMyToken(uid: String) {

        val token = Firebase.messaging.token.await()

        // Check whether the retrieved token matches the one on your server for this user's device

        // Example shown below with Firestore
        // Add token and timestamp to Firestore for this user
        val deviceToken = hashMapOf(
            "token" to token,
            "timestamp" to FieldValue.serverTimestamp(),
        )

        // Get user ID from Firebase Auth or your own server
        Firebase.firestore.collection("fcmTokens").document(uid)
            .set(deviceToken).await()
    }

    fun saveTokenFromJava(uid: String) {
        GlobalScope.launch {
            saveMyToken(uid)
        }
    }

}








/**
 * my Persist token to third-party servers.
 *
 * Modify this method to associate the user's FCM registration token with any server-side account
 * maintained by your application.
 *
 * @param token The new token.
 */
private fun sendTokenToServer(token: String?) {
    // If you're running your own server, call API to send token and today's date for the user

    // Example shown below with Firestore
    // Add token and timestamp to Firestore for this user
  //  val uid = Objects.requireNonNull(FirebaseAuth.getInstance().currentUser)
    val deviceToken = hashMapOf(
        "token" to token,
    //    "uid" to uid,
        "timestamp" to FieldValue.serverTimestamp(),
    )

    //ChatUtil.createChat("ZqnkGaeyk3UkRLRJH8Qh1tQiF7d2");
    // FirebaseDatabase.getInstance().getReference().child("Users").child("mdlfnx5Bo4dbpKOjwmUxaiyJzAY2")
    //       .child("chats").setValue(chatsUpd);
   // val uid = Objects.requireNonNull(FirebaseAuth.getInstance().currentUser)
    // Get user ID from Firebase Auth or your own server

    val uid = Objects.requireNonNull(FirebaseAuth.getInstance().currentUser)

    Firebase.firestore.collection("fcmTokens").document("uid".toString())
        .set(deviceToken)


}

object TokenProvider {
    @OptIn(DelicateCoroutinesApi::class)
    @JvmStatic
    suspend fun getFirebaseToken(): CompletableFuture<String> = GlobalScope.future {
        return@future FirebaseMessaging.getInstance().token.await()
    }



}

А вот код AndroidManifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:usesCleartextTraffic="true"
        android:supportsRtl="true"
        android:theme="@style/Theme.AndroidMessenger"
        tools:targetApi="31">

        <activity
            android:name=".RegisterActivity"
            android:exported="false" />
        <activity
            android:name=".LoginActivity"
            android:exported="true"></activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <activity
            android:name=".ChatActivity"
            android:exported="false"
            android:screenOrientation="sensor"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
                <action android:name="ACTIVITY_XPTO" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <activity
            android:name="fcmpushnotificationshttpv1.MainActivity"
            android:exported="true"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>


        <service
            android:name="fcmpushnotificationshttpv1.PushNotificationService"
            android:exported="false"
            >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />



    </application>




</manifest>

При этом, в моем приложении 2 пакета. Но отправка сообщений производится через сервис, написанный и находящийся в пакете fcmpushnotificationshttpv1. При этом при приеме сообщения в логе пишет другой пакет dem.corp.androidmessenger:

---------------------------- PROCESS STARTED (4173) for package dem.corp.androidmessenger ----------------------------
2025-02-14 15:33:53.802  4173-4258  0000                    dem.corp.androidmessenger            D  Received data: {}
(то есть указывает что принимает сервис сообщение, который (сервис) из другого пакета) Но! Log.D с сообщением : "Received data: {}" (вывод этого сообщения) находится в другом пакете - fcmpushnotificationshttpv1, где и сервис. Скажите, как изменить сервис по приему сообщений по умолчанию? ведь в манифесте верно указан нужный сервис: <service android:name="fcmpushnotificationshttpv1.PushNotificationService" И почему нет данных в Recieve Message ?

Второй способ, пробую: не помогает: Обнолвние, Юрий СПБ. я попробовал так: добавил класс DataBody в SendMessagesDTO.kt:

package fcmpushnotificationshttpv1

data class SendMessageDto(
    val to: String?,
    val data: DataBody,
    val notification: NotificationBody
)


data class DataBody(
    val idoffer: String

)

data class NotificationBody(
    val title: String,
    val body: String,
    val chatId: String,
    val click_action: String
)
Такой же класс Data я добавил в ChatViewModel.kt:

//@file:JvmName("ChatViewModel")
//@JvmMultifileClass
package fcmpushnotificationshttpv1

import android.annotation.SuppressLint
import android.content.ContentValues.TAG
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.ktx.Firebase
import com.google.firebase.messaging.ktx.messaging
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import retrofit2.HttpException
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.create
import java.io.IOException
import java.security.AccessController.getContext


class ChatViewModel: ViewModel() {

    var state by mutableStateOf(ChatState())
        private set

    private val api: FcmApi = Retrofit.Builder()
      //  .baseUrl("http://10.0.2.2:8084/")
        .baseUrl("http://109.195.103.21:8084/")
        .addConverterFactory(MoshiConverterFactory.create())
        .build()
        .create()

    init {
        viewModelScope.launch {
            Firebase.messaging.subscribeToTopic("chat").await()
        }
    }

    fun onRemoteTokenChange(newToken: String) {
        state = state.copy(
            remoteToken = newToken
        )
    }

    fun onSubmitRemoteToken() {
        state = state.copy(
            isEnteringToken = false
        )
    }

    fun onMessageChange(message: String) {
        state = state.copy(
            messageText = message
        )
    }

    @SuppressLint("RestrictedApi")
    fun sendMessage(isBroadcast: Boolean, tokenTo: String, title: String, body: String, chatId: String, click_action: String) {
        Log.d(TAG, "sendMessage: chatId: $chatId")

      //  Toast.makeText(Context!, "token", Toast.LENGTH_SHORT).show()


        viewModelScope.launch {




            val messageDto = SendMessageDto(
               // to = if(isBroadcast) null else state.remoteToken,
                to = tokenTo,
                data = DataBody( idoffer = "41"),
                notification = NotificationBody(
                    title = title,
                   // body = state.messageText
                    body = body,
                            chatId = chatId,
                    click_action = click_action
                )
            )
            Log.d(TAG, "sendMessage: ")

            Log.w(
                FragmentManager.TAG,
                "entering function : 2222 sendMessage is BROAADCAST = $isBroadcast"
            )
           // api.sendMessage(messageDto)


            try {
                if(isBroadcast) {
                    api.broadcast(messageDto)
                } else {
                    api.sendMessage(messageDto)

                    Log.w(
                        FragmentManager.TAG,
                        "entering function : 2222 sendMessage"
                    )
                }

                state = state.copy(
                   messageText = ""
                )
                //state  = "meggase text my";

            } catch(e: HttpException) {
                e.printStackTrace()
            } catch(e: IOException) {
                e.printStackTrace()
            }
        }
    }
}

Такой же класс добавил на сервер KTOR:

import com.google.firebase.messaging.Message
import com.google.firebase.messaging.Notification
import io.ktor.http.*
import kotlinx.serialization.Serializable

@Serializable
data class SendMessageDto(
    val to: String?,
    val data: Databody,
    val notification: NotificationBody

)

@Serializable
data class Databody(
    val idoffer: String

)

@Serializable
data class NotificationBody(
    val title: String,
    val body: String,
    val chatId: String,
    val  click_action: String
)

fun SendMessageDto.toMessage(): Message {
    return Message.builder()
       // .putData("ggg", "dddd")
        .setNotification(
            Notification.builder()
                .setTitle(notification.title)
                .setBody((notification.body))
               // .setClickaction(notification.click_action)
              //  .
                .build()

        )
        .apply {
            if(to == null) {
                setTopic("chat")
            }
            else {
                setToken(to)
            }
        }
        .build()
}

И пытаюсь в mainActivity поймать idoffer:

    //my
        String ifOffer = "";
        Intent startingIntent = getIntent();
        ifOffer = startingIntent.getStringExtra("idoffer");
        Log.d(TAG, "idOffer = v main activity: " + ifOffer);

и пишет значение NULL Юрий, что делать? подскажите, почему значение null? верно ли я догадываюсь что в сервере ktor надо еще как-то добавить переменную данных? Я точку нажимаю и есть SetBode, setTitle. Set Data нету: введите сюда описание изображения


Ответы (1 шт):

Автор решения: ЮрийСПб

Про пакеты не вчитывался и ничего не понял. Про приём данных из пуша: скорее всего у вас проблема в том, что надо с бэка слать Data пуши, а не Notification пуши. Notification работают по разному в зависимости от состояния приложения и в onMessageReceived не всегда приходят. Моя многолетняя практика говорит о том, что их следует использовать никогда. И всегда слать Data пуши. Они работают как надо вне зависимости от состояния приложения

→ Ссылка