0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterでFCMトークン取得時に TOO_MANY_REGISTRATIONS が発生した話

0
Last updated at Posted at 2026-02-16

起きた事象

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')),
      ),
    );
  }
}

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?