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

Flutter前后台稳定接收SMS消息问题:telephony包异常咨询

Flutter 稳定实现前后台及关闭状态接收SMS消息方案

针对你使用telephony包出现的接收不稳定问题,以下是分平台的稳定实现方案,覆盖前台、后台及应用关闭状态:


Android 端核心配置与实现

1. 权限声明与动态申请

在AndroidManifest.xml中添加必要权限:

<!-- 基础短信接收/读取权限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<!-- Android 12+ 后台通知权限(保障前台服务运行) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 允许应用开机自启动(保障重启后仍能监听) -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

代码中动态申请权限(Android 6.0+ 强制要求):

import 'package:permission_handler/permission_handler.dart';

Future<void> requestSmsPermissions() async {
  final status = await [
    Permission.sms,
    Permission.postNotifications,
  ].request();
  if (status[Permission.sms] != PermissionStatus.granted) {
    // 处理权限拒绝逻辑,如引导用户前往设置开启
  }
}

2. 自定义高优先级BroadcastReceiver

放弃依赖telephony包的默认接收器,自定义广播接收器确保优先级最高,避免被系统或其他应用拦截:
在AndroidManifest.xml中注册接收器:

<receiver
    android:name=".SmsReceiver"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.BROADCAST_SMS">
    <intent-filter android:priority="999">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

创建SmsReceiver.kt类处理短信:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.provider.Telephony
import io.flutter.plugin.common.MethodChannel

class SmsReceiver : BroadcastReceiver() {
    companion object {
        lateinit var channel: MethodChannel
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
            val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
            for (msg in messages) {
                val sender = msg.displayOriginatingAddress
                val content = msg.displayMessageBody
                channel.invokeMethod("onSmsReceived", mapOf(
                    "sender" to sender,
                    "content" to content
                ))
            }
        }
    }
}

在Flutter的MainActivity中初始化MethodChannel:

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "sms_receiver"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        SmsReceiver.channel = channel
    }
}

3. 后台保活策略

Android系统的Doze模式、App Standby会限制后台进程,需通过以下方式提升优先级:

  • 启动前台服务(Android 8.0+ 强制要求前台服务带通知):
    创建SmsForegroundService.kt:
    import android.app.Notification
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.app.Service
    import android.content.Intent
    import android.os.Build
    import android.os.IBinder
    
    class SmsForegroundService : Service() {
        private val CHANNEL_ID = "sms_listener_channel"
    
        override fun onCreate() {
            super.onCreate()
            createNotificationChannel()
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            val notification = Notification.Builder(this, CHANNEL_ID)
                .setContentTitle("短信监听中")
                .setContentText("后台持续接收短信")
                .setSmallIcon(R.drawable.ic_notification)
                .setPriority(Notification.PRIORITY_LOW)
                .build()
            startForeground(1, notification)
            return START_STICKY
        }
    
        private fun createNotificationChannel() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val channel = NotificationChannel(
                    CHANNEL_ID,
                    "短信监听服务",
                    NotificationManager.IMPORTANCE_LOW
                )
                val manager = getSystemService(NotificationManager::class.java)
                manager.createNotificationChannel(channel)
            }
        }
    
        override fun onBind(intent: Intent): IBinder? = null
    }
    
    在AndroidManifest.xml中注册服务:
    <service android:name=".SmsForegroundService" android:foregroundServiceType="dataSync" />
    
    Flutter端启动服务:
    await _channel.invokeMethod("startForegroundService");
    
  • 引导用户加入厂商白名单:小米、华为、OPPO等厂商有独立的后台管理,需提示用户将应用加入「后台保护」或「自启动许可」列表。

iOS 端核心配置与实现

1. 权限与Background Modes配置

在Info.plist中添加权限描述与后台模式:

<!-- 短信权限申请说明 -->
<key>NSMessagesUsageDescription</key>
<string>需要访问短信以接收验证码或通知</string>
<!-- 后台模式配置 -->
<key>UIBackgroundModes</key>
<array>
    <string>remote-notification</string>
</array>

2. 原生短信监听实现

iOS无法通过广播接收后台短信,需通过CoreTelephony框架或短信拦截扩展实现:

  • 使用CoreTelephony监听(适用于iOS 13及以下,iOS 14+需企业签名):
    在AppDelegate.swift中添加监听:
    import UIKit
    import Flutter
    import CoreTelephony
    
    @UIApplicationMain
    @objc class AppDelegate: FlutterAppDelegate {
        private let CHANNEL = "sms_receiver"
        private var messageCenter: CTMessageCenter!
    
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
            let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
            
            messageCenter = CTMessageCenter.shared()
            messageCenter.setDelegate(self, queue: DispatchQueue.main)
            
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    }
    
    extension AppDelegate: CTMessageCenterDelegate {
        func messageCenter(_ center: CTMessageCenter, didReceive message: CTMessage) {
            let sender = message.sender ?? ""
            let content = message.text ?? ""
            let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: window!.rootViewController as! FlutterViewController)
            channel.invokeMethod("onSmsReceived", arguments: ["sender": sender, "content": content])
        }
    }
    
  • 短信拦截扩展(适用于所有iOS版本,需App Store审核):创建SMS Filter Extension,在扩展中拦截短信并通过App Groups与主应用通信。

Flutter 端统一处理

通过MethodChannel接收原生端传递的短信数据:

import 'package:flutter/services.dart';

class SmsReceiver {
  static const MethodChannel _channel = MethodChannel('sms_receiver');
  static Function(String sender, String content)? onSmsReceived;

  static void init() {
    _channel.setMethodCallHandler((call) async {
      if (call.method == 'onSmsReceived') {
        final sender = call.arguments['sender'] as String;
        final content = call.arguments['content'] as String;
        onSmsReceived?.call(sender, content);
      } else if (call.method == 'startForegroundService') {
        // 可选:处理启动前台服务的回调
      }
    });
  }
}

// 在main函数中初始化
void main() {
  runApp(const MyApp());
  SmsReceiver.init();
  SmsReceiver.onSmsReceived = (sender, content) {
    print("收到短信:来自$sender,内容:$content");
    // 处理短信逻辑,如解析验证码
  };
}

稳定性调试与排查

  • Android:
    • 使用adb shell dumpsys deviceidle force-idle进入Doze模式,测试短信接收。
    • 检查Logcat中的广播接收日志,确认是否被系统拦截。
    • 验证应用是否在厂商后台白名单中。
  • iOS:
    • 测试后台状态时,需使用真机(模拟器不支持短信接收)。
    • iOS 14+需确认用户已授予短信读取权限。

内容的提问来源于stack exchange,提问作者negin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 11:30:55