如何在Flutter中实现Android后台周期性发送SMS
Flutter Android后台发送SMS的可行方案及代码优化
一、两种可行实现路径
1. 第三方Flutter库快速实现
可以使用维护中的sms_maintained库(替代已停止维护的sms库),它封装了Android和iOS的SMS发送逻辑,基础用法如下:
- 添加依赖到
pubspec.yaml:
dependencies: sms_maintained: ^2.2.0
- 发送SMS的核心代码:
import 'package:sms_maintained/sms.dart'; void sendBackgroundSms() async { SmsSender sender = SmsSender(); String phoneNumber = "+923000000000"; String message = "Automatic message"; try { await sender.sendSms(SmsMessage(phoneNumber, message)); print("SMS sent"); } catch (e) { print("Failed to send SMS: $e"); } }
但要实现后台持续发送,仍需结合Android的Foreground Service(见下文),因为Flutter引擎在后台可能被系统挂起。
2. 自定义MethodChannel + Android Foreground Service(深度定制)
你当前的代码依赖Flutter Widget的Timer和Activity上下文,APP退到后台后会失效。下面是优化方案:
问题分析
- Flutter的
Timer.periodic运行在UI线程,APP进入后台后Flutter引擎可能被暂停,定时器停止。 MainActivity中的发送逻辑依赖Activity上下文,后台时Activity可能被销毁,导致发送失败。
优化步骤
步骤1:Android端添加Foreground Service
创建SmsService.kt处理后台SMS发送和定时任务:
import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Intent import android.os.Build import android.os.IBinder import android.telephony.SmsManager import android.util.Log import androidx.core.app.NotificationCompat import io.flutter.embedding.android.FlutterActivity class SmsService : android.app.Service() { private val CHANNEL_ID = "SmsServiceChannel" private val NOTIFICATION_ID = 101 private var timer: java.util.Timer? = null private val phoneNumbers = listOf("+923000000000") private val message = "Automatic message" override fun onCreate() { super.onCreate() createNotificationChannel() startForeground(NOTIFICATION_ID, buildNotification()) startSmsTimer() } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, "SMS Service", NotificationManager.IMPORTANCE_LOW ).apply { description = "后台发送SMS服务" } val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) } } private fun buildNotification(): Notification { val intent = Intent(this, FlutterActivity::class.java) val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) return NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("SMS发送服务") .setContentText("正在后台自动发送SMS") .setSmallIcon(android.R.drawable.ic_dialog_email) .setContentIntent(pendingIntent) .build() } private fun startSmsTimer() { timer = java.util.Timer() timer?.scheduleAtFixedRate(object : java.util.TimerTask() { override fun run() { sendSmsToAllNumbers() } }, 0, 2000) // 每2秒发送一次 } private fun sendSmsToAllNumbers() { val smsManager = SmsManager.getDefault() for (number in phoneNumbers) { try { smsManager.sendTextMessage(number, null, message, null, null) Log.d("SMS_SERVICE", "Sent to $number") } catch (e: Exception) { Log.e("SMS_SERVICE", "Failed to send to $number: ${e.message}") } } } override fun onDestroy() { timer?.cancel() super.onDestroy() } override fun onBind(intent: Intent): IBinder? { return null } }
步骤2:修改MainActivity.kt,添加Service启动逻辑
class MainActivity : FlutterActivity() { private val CHANNEL = "sendSms" private val SMS_PERMISSION_CODE = 1 override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "startSmsService" -> { if (checkSmsPermission() && checkForegroundServicePermission()) { startService(Intent(this, SmsService::class.java)) result.success("Service started") } else { requestPermissions(result) } } "stopSmsService" -> { stopService(Intent(this, SmsService::class.java)) result.success("Service stopped") } else -> result.notImplemented() } } } private fun checkSmsPermission(): Boolean { return ContextCompat.checkSelfPermission(this, android.Manifest.permission.SEND_SMS) == android.content.pm.PackageManager.PERMISSION_GRANTED } private fun checkForegroundServicePermission(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { ContextCompat.checkSelfPermission(this, android.Manifest.permission.FOREGROUND_SERVICE) == android.content.pm.PackageManager.PERMISSION_GRANTED } else { true } } private fun requestPermissions(result: MethodChannel.Result) { val permissions = mutableListOf(android.Manifest.permission.SEND_SMS) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { permissions.add(android.Manifest.permission.FOREGROUND_SERVICE) } ActivityCompat.requestPermissions(this, permissions.toTypedArray(), SMS_PERMISSION_CODE) result.error("PERMISSION_REQUIRED", "需要SMS和前台服务权限", null) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == SMS_PERMISSION_CODE) { val allGranted = grantResults.all { it == android.content.pm.PackageManager.PERMISSION_GRANTED } if (allGranted) { Log.d("SMS_PERMISSION", "所有权限已授予") } else { Log.e("SMS_PERMISSION", "权限被拒绝") } } } }
步骤3:修改Flutter端main.dart,调用Service
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class SendSms extends StatefulWidget { const SendSms({super.key}); @override _SendSmsState createState() => _SendSmsState(); } class _SendSmsState extends State<SendSms> { static const platform = MethodChannel('sendSms'); bool _serviceRunning = false; Future<void> _startService() async { try { final String result = await platform.invokeMethod('startSmsService'); setState(() => _serviceRunning = true); print(result); } on PlatformException catch (e) { print("Failed to start service: ${e.toString()}"); } } Future<void> _stopService() async { try { final String result = await platform.invokeMethod('stopSmsService'); setState(() => _serviceRunning = false); print(result); } on PlatformException catch (e) { print("Failed to stop service: ${e.toString()}"); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("SMS后台发送")), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(_serviceRunning ? "后台服务运行中..." : "点击启动后台服务"), const SizedBox(height: 20), ElevatedButton( onPressed: _serviceRunning ? _stopService : _startService, child: Text(_serviceRunning ? "停止服务" : "启动服务"), ), ], ), ), ); } }
步骤4:配置AndroidManifest.xml
添加权限和Service声明:
<uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application ...> <service android:name=".SmsService" android:foregroundServiceType="dataSync" /> </application>
二、关键注意事项
- Android 8.0+对后台服务有严格限制,必须使用Foreground Service并显示通知,否则会被系统杀死。
- 定时任务尽量放在Android原生组件(如Service、WorkManager)中,不要依赖Flutter的Timer,避免后台引擎挂起导致任务中断。
- 测试时需在真机上进行,模拟器可能不支持SMS发送功能。
内容的提问来源于stack exchange,提问作者Muhammad Osama
相关产品推荐
相关产品推荐

