起きた事象
Flutter + Firebase Messagingを使用していてCrashlyticsを取得していたところ
Firebase token error TOO_MANY_REGISTRATIONSが発生していました。
ユーザー(開発者ではない)ができる対応
他のアプリを削除してもらう
原因
tokenを取得失敗時のキャッチでdeleteToken(),getToken()をしていたことでした。
むしろ悪化する
端末から該当アプリをアンインストールする
-> アンインストールの度に新しいtokenをセットしてしまうので
やってしまっていた実装
Future<String?> getToken() async {
try {
return await FirebaseMessaging.instance.getToken();
} catch (e) {
// ここが問題
await FirebaseMessaging.instance.deleteToken();
return await FirebaseMessaging.instance.getToken();
}
}
なぜダメか?
deleteToken()が新しく登録するtokenを再生成する操作だから。
これらが、短期間で繰り返されると、tokenが大量に登録されてTOO_MANY_REGISTRATIONS状態になってしまう
こんな感じにするといいかも
-
onTokenRefresh()内でtokenのセットをする - このやり方はfirebaseのベストプラクティスです
app.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers.dart';
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
@override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
StreamSubscription<String>? _subscription;
@override
void initState() {
super.initState();
_syncInitialToken();
_listenTokenRefresh();
}
Future<void> _syncInitialToken() async {
final fcmRepository = ref.read(fcmRepositoryProvider);
final token = await fcmRepository.getToken();
if (token == null) return;
await syncServer(token);
}
void _listenTokenRefresh() {
final fcmRepository = ref.read(fcmRepositoryProvider);
_subscription = fcmRepository
.onTokenRefresh()
.distinct() // 同じトークンが再通知されないように
.listen(
(fcmToken) async {
await syncServer(fcmToken);
}
);
}
Future<void> syncServer(String token) async {
final sharedPreferenceClient = ref.read(sharedPreferenceClientProvider);
final serverRepository = ref.read(serverRepositoryProvider);
final cached = await sharedPreferenceClient.getToken();
if (cached == token) return;
await serverRepository.registerFcmToken(token);
await sharedPreferenceClient.setToken(token);
}
@override
void dispose() {
_subscription?.cancel();
_subscription = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: const Center(child: Text('App')),
),
);
}
}
参考