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中注册服务:
Flutter端启动服务:<service android:name=".SmsForegroundService" android:foregroundServiceType="dataSync" />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
相关产品推荐
相关产品推荐

