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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:24:55