Мы сталкиваемся с критической проблемой с обменом обменом облачными сообщениями 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
Уведомления об обмене обменами Firebase (FCM) задержаны или не доставляются (до 1 часа задержки) ⇐ Android
Форум для тех, кто программирует под Android
1757573553
Anonymous
Мы сталкиваемся с критической проблемой с обменом обменом облачными сообщениями Firebase (FCM) в нашем приложении Flutter. < /p>
[b] Проблема: < /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/test/messages: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; }
}
[b] Flutter Code [/b]
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
Подробнее здесь: [url]https://stackoverflow.com/questions/79761554/firebase-cloud-messaging-fcm-notifications-delayed-or-not-delivered-up-to-1-h[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия