Уведомления об обмене обменами Firebase (FCM) задержаны или не доставляются (до 1 часа задержки)Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Уведомления об обмене обменами Firebase (FCM) задержаны или не доставляются (до 1 часа задержки)

Сообщение Anonymous »

Мы сталкиваемся с критической проблемой с обменом обменом облачными сообщениями Firebase (FCM) в нашем приложении Flutter. < /p>
Проблема: < /strong> < /p>

[*] Наше приложение получает около 25 000 уведомлений в день. Второе. задерживаются до 1 часа до достижения устройств. /> < /li>
< /ul>
Важное наблюдение: < /strong> < /p>

Когда мы отправляем несколько уведомлений на одно устройство, нет
задержка или отсутствующие уведомления. Уведомления
одновременно для нескольких устройств. < /p>
< /li>
< /ul>
Что мы подтвердили: < /strong> < /p>

Бэкэнд интегрирован с FCM API правильно. Токены действительно. Information:[/b]

We are using Flutter for the client application.

[*]Backend is written in C# and sends notifications using the FCM
API.

I have attached the Flutter notification handling code и
код триггера по уведомлению
для справки. /> Существует ли какая -либо скрытая дроссельная или ограничения с свободным планом
fcm, который может объяснить это? Кратше всего, поскольку наши пользователи полагаются на уведомления в реальном времени, а задержки до часа делают услугу непригодным.using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;

class FCMClient
{
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult(); // Compatible with .NET Framework
}

static async Task MainAsync(string[] args)
{
string connectionString = "";
string serverKey = GetAccessTokenAsync().Result;

while (true)
{
try
{
List notificationData = RetrieveNotificationData(connectionString);
await SendNotifications(notificationData, serverKey, connectionString);
}
catch (Exception ex)
{
Console.WriteLine("An error occurred: " + ex.Message);
}

// await Task.Delay(TimeSpan.FromSeconds(30)); // Optional delay
}
}

private static string jsonString = @"";

public static async Task GetAccessTokenAsync()
{
string connectionString = "";
try
{
Console.WriteLine("Notification App Started...");
byte[] byteArray = Encoding.UTF8.GetBytes(jsonString.Replace('\'', '"'));
using (MemoryStream stream = new MemoryStream(byteArray))
{
GoogleCredential credential = GoogleCredential.FromStream(stream)
.CreateScoped("https://www.googleapis.com/auth/firebase.messaging");

var token = await credential.UnderlyingCredential.GetAccessTokenForRequestAsync();
Console.WriteLine("Token: " + token);
// await InsertDeviceToken(connectionString, token);
return token;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error fetching access token: {ex.Message}");
return null;
}
}

static List RetrieveNotificationData(string connectionString)
{
List notificationData = new List();

try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("[SEND_NotificationIMPCust]", connection))
{
command.CommandType = CommandType.StoredProcedure;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
NotificationData data = new NotificationData
{
UserId = reader["userid"].ToString(),
DeviceToken = reader["deviceToken"].ToString(),
NotificationId = Convert.ToInt32(reader["notification_id"]),
NotificationText = reader["notification_text"].ToString(),
NotificationAlert = reader["notification_alert"].ToString(),
NotificationUser = reader["notification_user"].ToString(),
RegistrationNo = reader["registration_no"].ToString()
};

notificationData.Add(data);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error retrieving notification data: " + ex.Message);
}

return notificationData;
}

static async Task SendNotifications(List notificationData, string serverKey, string connectionString)
{
using (HttpClient client = new HttpClient())
{
foreach (NotificationData data in notificationData)
{
bool retry;
do
{
retry = false;
try
{
string json = $@"
{{
""message"": {{
""token"": ""{data.DeviceToken}"",
""notification"": {{
""title"": ""Notification - {data.RegistrationNo}"",
""body"": ""{data.NotificationText}""
}},
""data"": {{
""route"": ""Autonotification""
}}
}}
}}";

client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + serverKey);
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");

Console.WriteLine($"[{DateTime.Now}] Sending message...");

HttpResponseMessage response = await client.PostAsync(
"https://fcm.googleapis.com/v1/projects/ ... sages:send",
new StringContent(json, Encoding.UTF8, "application/json")
);

string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("Response: " + responseBody);

if (IsTokenExpired(responseBody))
{
Console.WriteLine("Access token expired. Getting new token...");
serverKey = await GetAccessTokenAsync();
retry = true;
}
else if (responseBody.Contains("UNREGISTERED"))
{
Console.WriteLine("Token unregistered. Deleting from DB...");
await DeleteUnregisteredToken(connectionString, data.DeviceToken);
}
}
catch (Exception ex)
{
Console.WriteLine("Error sending FCM: " + ex.Message);
}
} while (retry);
}
}
}

static bool IsTokenExpired(string responseBody)
{
return responseBody.Contains("ACCESS_TOKEN_EXPIRED") || responseBody.Contains("CREDENTIALS_MISSING");
}

static async Task DeleteUnregisteredToken(string connectionString, string token)
{
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("DELETE FROM [MIS].[dbo].[TBL_ANDRIOD_REGISTER_DEVICE] WHERE deviceToken = @DeviceToken", connection))
{
command.Parameters.AddWithValue("@DeviceToken", token);
int rowsAffected = await command.ExecuteNonQueryAsync();
Console.WriteLine(rowsAffected > 0
? "Unregistered token removed."
: "No matching token found.");
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error deleting token: " + ex.Message);
}
}

static async Task InsertDeviceToken(string connectionString, string deviceToken)
{
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
string insertQuery = @"
IF NOT EXISTS (SELECT 1 FROM DeviceTokens WHERE DeviceToken = @DeviceToken)
BEGIN
INSERT INTO DeviceTokens (DeviceToken) VALUES (@DeviceToken)
END";

using (SqlCommand command = new SqlCommand(insertQuery, connection))
{
command.Parameters.AddWithValue("@DeviceToken", deviceToken);
await command.ExecuteNonQueryAsync();
Console.WriteLine($"Device token '{deviceToken}' inserted.");
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error inserting device token: " + ex.Message);
}
}
}

class NotificationData
{
public string UserId { get; set; }
public string DeviceToken { get; set; }
public int NotificationId { get; set; }
public string NotificationText { get; set; }
public string NotificationAlert { get; set; }
public string NotificationUser { get; set; }
public string RegistrationNo { get; set; }
}

Flutter Code
import 'dart:async';
import 'dart:developer';

import 'package:test/Constants/imports.dart';
import 'package:get/get.dart';

class NotificationService {
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
static const channelID = 'high_importance_channel';
bool isFlutterLocalNotificationsInitialized = false;
static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future setupFlutterNotifications() async {
try {
if (isFlutterLocalNotificationsInitialized) {
return;
}
initializeNotifications();
listenUpdateNotificationCall();
// Ensure iOS foreground presentation shows alerts, sound, and badge
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
// Request FCM notification permissions (especially iOS)
await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: false,
badge: true,
sound: true,
carPlay: false,
criticalAlert: false,
provisional: false,
);
/////
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
_handleOnTap(message);
}
});

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Show a local notification for both notification and data-only messages
showLocalNotification(message);
});

FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_handleOnTap(message);
});
isFlutterLocalNotificationsInitialized = true;
} on Exception catch (e) {
print("error try...$e");
log("error try.....$e");
}
}

void initializeNotifications() async {
var androidInitialize =
const AndroidInitializationSettings('@mipmap/ic_launcher');
var iosInitialize = const DarwinInitializationSettings();
requestNotificationPermissions();
var initializeSettings =
InitializationSettings(android: androidInitialize, iOS: iosInitialize);
flutterLocalNotificationsPlugin.initialize(initializeSettings,
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse);
}

//PERMISSION FOR NOTIFICATIONS
static Future requestNotificationPermissions() async {
AndroidNotificationChannel channel = const AndroidNotificationChannel(
channelID,
channelID,
playSound: true,
importance: Importance.max,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
await [
Permission.notification,
].request();
}

Future removeAppFromBatteryOptimization() async {
var status = await Permission.ignoreBatteryOptimizations.status;
if (status.isDenied || status.isRestricted) {
// Request permission to disable battery optimization
status = await Permission.ignoreBatteryOptimizations.request();
}
}

void onDidReceiveNotificationResponse(NotificationResponse response) async {
final String? payload = response.payload;

if (payload != null) {
try {
// Parse the JSON payload
final Map data = jsonDecode(payload);
if (data.isNotEmpty &&
data.containsKey("route") &&
data.containsValue("Autonotification")) {
Get.offAll(BottomNavigationMainScreen(
index: 2,
));
}
} catch (e) {
log('Error parsing notification payload: $e');
}
}
}

_handleOnTap(RemoteMessage message) {
try {
if (message.data.isNotEmpty &&
message.data.containsKey("route") &&
message.data.containsValue("Autonotification")) {
Get.offAll(BottomNavigationMainScreen(
index: 2,
));
}
} on Exception catch (e) {
print("error......$e");
// TODO
}
}

void showLocalNotification(RemoteMessage message) async {
try {
final RemoteNotification? remote = message.notification;
final String? title = remote?.title ?? message.data['title'] as String?;
final String? body = remote?.body ??
(message.data['body'] as String? ??
message.data['message'] as String?);

// Optional: imageUrl available if needed in future
// final String? imageUrl = Platform.isAndroid
// ? remote?.android?.imageUrl
// : remote?.apple?.imageUrl;

final NotificationDetails details = NotificationDetails(
android: AndroidNotificationDetails(
channelID,
channelID,
channelDescription: 'App notifications',
importance: Importance.max,
priority: Priority.max,
icon: 'launch_background',
),
iOS: const DarwinNotificationDetails(),
);

// Use a stable id if provided, else hash
final int id = (message.data['id'] is int)
? message.data['id'] as int
: (remote?.hashCode ?? DateTime.now().millisecondsSinceEpoch);

if (title != null || body != null) {
await flutterLocalNotificationsPlugin.show(
id,
title,
body,
details,
payload: jsonEncode(message.data),
);
}
} catch (e) {}
}

StreamSubscription? _tokenRefreshSubscription;

void listenUpdateNotificationCall() {
// Cancel existing subscription if any

_tokenRefreshSubscription?.cancel();

_tokenRefreshSubscription =
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) async {
var userId = await SharedPref().getIntVariable(AppKeys.userId);
var userToken = await SharedPref().getStringVariable(AppKeys.userToken);
if (userId != null && userToken != null) {
log("newToken::::: $newToken");
var deviceId = await GlobalFunctions().getId();

await AuthApis.instance.sendFcmToken(
userId: userId,
userToken: userToken,
fcmToken: newToken,
deviceId: deviceId!);
}
});
}

Future getFCMToken() async {
final token = await FirebaseMessaging.instance.getToken();
logSuccess("FCM Token: $token");
return token;
}

void showNotifyTest() {
try {
flutterLocalNotificationsPlugin.show(
0,
" notification.title",
"notification.body",
const NotificationDetails(
android: AndroidNotificationDetails(
channelID,
channelID,
icon: 'launch_background',
),
),
payload: 'https://dotsinc.org');
} catch (e) {}
}
}
< /code>
android manifest.xml






























Подробнее здесь: https://stackoverflow.com/questions/797 ... -up-to-1-h
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»