You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android设备接收重复FCM通知,桌面端正常的问题求助

Android设备接收重复FCM通知,桌面端正常的问题求助

我现在遇到一个很头疼的问题:我做了一个Flask web应用,用Google Sheets API扫描校车位置表格,判断校车所在区域,然后给用户发送包含校车编号、所在象限/区域以及相邻校车的通知。这个应用在桌面设备上完全正常,但在Android上会收到重复的通知——其中一条带网站图标,另一条没有。

一开始我以为是ngrok隧道的问题(我用它把手机连到本地HTTPS的Flask服务),但桌面端用ngrok访问完全没问题,不会发重复通知,所以应该不是ngrok的锅。

我把代码精简到最核心的版本了,保留了问题复现的关键部分,现在这个简化版也会出现同样的重复通知问题:

Flask App代码

from flask import Flask, request, jsonify, render_template, send_from_directory
import firebase_admin
from firebase_admin import credentials, messaging
from flask_cors import CORS
import os
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

app = Flask(__name__,
    template_folder='templates',
    static_folder='static',
    static_url_path=''
)
CORS(app)

# Initialize Firebase Admin SDK
cred = credentials.Certificate('Core/firetoken.json')  # Your Firebase credentials file
firebase_admin.initialize_app(cred)

@app.route('/firebase-messaging-sw.js')
def sw():
    response = send_from_directory(app.static_folder, 'firebase-messaging-sw.js')
    response.headers['Content-Type'] = 'application/javascript'
    response.headers['Service-Worker-Allowed'] = '/'
    return response

@app.route('/')
def home():
    return render_template('index.html',
        firebase_config=dict(
            api_key=os.getenv('FIREBASE_API_KEY'),
            auth_domain=os.getenv('FIREBASE_AUTH_DOMAIN'),
            project_id=os.getenv('FIREBASE_PROJECT_ID'),
            storage_bucket=os.getenv('FIREBASE_STORAGE_BUCKET'),
            messaging_sender_id=os.getenv('FIREBASE_MESSAGING_SENDER_ID'),
            app_id=os.getenv('FIREBASE_APP_ID'),
            measurement_id=os.getenv('FIREBASE_MEASUREMENT_ID')
        ),
        vapid_key=os.getenv('VAPID_KEY')
    )

@app.route('/store_token', methods=['POST'])
def store_token():
    data = request.json
    token = data.get('token')
    
    if not token:
        return jsonify({'error': 'Token is required'}), 400

    try:
        # Send a test notification
        message = messaging.Message(
            notification=messaging.Notification(
                title="Test Notification",
                body="This is a test notification!"
            ),
            token=token
        )
        messaging.send(message)
        return jsonify({'status': 'Notification sent successfully'})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)

HTML Template代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Notification Test</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin: 10px 0;
        }
        #status {
            margin: 20px 0;
            padding: 10px;
            border-radius: 4px;
        }
        .success { background-color: #dff0d8; color: #3c763d; }
        .error { background-color: #f2dede; color: #a94442; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Notification Test</h1>
        <button id="send-notification">Send Test Notification</button>
        <p id="status"></p>
    </div>

    <script type="module">
        import { initializeApp } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-app.js";
        import { getMessaging, getToken, onMessage } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-messaging.js";

        const firebaseConfig = {
            apiKey: "{{ firebase_config.api_key }}",
            authDomain: "{{ firebase_config.auth_domain }}",
            projectId: "{{ firebase_config.project_id }}",
            storageBucket: "{{ firebase_config.storage_bucket }}",
            messagingSenderId: "{{ firebase_config.messaging_sender_id }}",
            appId: "{{ firebase_config.app_id }}",
            measurementId: "{{ firebase_config.measurement_id }}"
        };

        const vapidKey = "{{ vapid_key }}";

        try {
            const app = initializeApp(firebaseConfig);
            const messaging = getMessaging(app);

            // Register service worker
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('/firebase-messaging-sw.js')
                    .then(registration => console.log('Service Worker registered'))
                    .catch(err => console.error('Service Worker registration failed:', err));
            }

            document.getElementById('send-notification').addEventListener('click', async () => {
                try {
                    const permission = await Notification.requestPermission();
                    if (permission === 'granted') {
                        const currentRegistration = await navigator.serviceWorker.getRegistration();
                        const token = await getToken(messaging, { 
                            vapidKey: vapidKey,
                            serviceWorkerRegistration: currentRegistration
                        });

                        const response = await fetch('/store_token', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ token: token })
                        });

                        const result = await response.json();
                        if (!response.ok) throw new Error(result.error);

                        document.getElementById('status').innerText = 'Notification sent successfully!';
                        document.getElementById('status').className = 'success';
                    } else {
                        throw new Error('Notification permission denied');
                    }
                } catch (error) {
                    document.getElementById('status').innerText = `Error: ${error.message}`;
                    document.getElementById('status').className = 'error';
                }
            });

            // Listen for messages
            onMessage(messaging, (payload) => {
                document.getElementById('status').innerText = `Received: ${payload.notification.title} - ${payload.notification.body}`;
                document.getElementById('status').className = 'success';
            });

        } catch (error) {
            console.error('Initialization error:', error);
            document.getElementById('status').innerText = `Error: ${error.message}`;
            document.getElementById('status').className = 'error';
        }
    </script>
</body>
</html>

问题的具体表现:Android设备上会收到两条内容完全一致的测试通知,其中一条显示网站的favicon图标,另一条没有任何图标,而桌面端只会收到一条正常的通知。

备注:内容来源于stack exchange,提问作者DavidGordiienko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 03:23:20