如何从Android BroadcastReceiver启动Flutter处理流程?
如何从Android静态BroadcastReceiver启动Flutter处理流程?
需求背景
我需要实现一款Flutter应用,满足以下特性:
- 被Android端广播动作
ACL_CONNECTED触发 - 在Dart层处理已连接设备的信息,根据结果启动带通知的前台服务(服务可持续处理任务,点击通知可打开应用UI)
原本计划通过AndroidManifest.xml注册静态BroadcastReceiver,在onReceive方法中把逻辑移交到Dart层,但遇到核心问题:静态Receiver触发时,Flutter的MethodChannel尚未初始化,无法通过常规的跨平台通道传递数据;若改用动态注册Receiver,虽能保证Dart回调就绪,但存在应用被系统杀死的风险。
可行解决方案
方案一:动态注册BroadcastReceiver(保证Flutter环境就绪)
此方案确保只有当应用启动后,广播接收器才生效,MethodChannel已完成初始化,可正常传递数据到Dart层。
- Flutter端初始化MethodChannel并处理回调
import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; final MethodChannel _deviceChannel = MethodChannel('com.example/device_channel'); void main() { WidgetsFlutterBinding.ensureInitialized(); // 设置方法处理器,接收Android端传来的设备连接信息 _deviceChannel.setMethodCallHandler((call) async { if (call.method == 'onDeviceConnected') { // 解析设备信息 final Map<String, dynamic> deviceInfo = call.arguments as Map<String, dynamic>; // 调用Android端方法启动前台服务 await _deviceChannel.invokeMethod('startForegroundService', deviceInfo); // 执行Dart层业务逻辑 handleDeviceConnected(deviceInfo); } }); runApp(const MyApp()); } void handleDeviceConnected(Map<String, dynamic> deviceInfo) { // 自定义设备连接后的处理逻辑 }
- Android端动态注册广播接收器
import android.bluetooth.BluetoothDevice import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private lateinit var deviceReceiver: BroadcastReceiver private lateinit var channel: MethodChannel override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example/device_channel") // 实现广播接收器逻辑 deviceReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == BluetoothDevice.ACTION_ACL_CONNECTED) { val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) val deviceInfo = mapOf( "name" to device?.name, "address" to device?.address ) // 传递数据到Dart层 channel.invokeMethod("onDeviceConnected", deviceInfo) } } } // 注册广播接收器 val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED) registerReceiver(deviceReceiver, filter) } override fun onDestroy() { super.onDestroy() // 注销接收器,避免内存泄漏 unregisterReceiver(deviceReceiver) } }
- 实现Android前台服务
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 androidx.core.app.NotificationCompat class DeviceForegroundService : android.app.Service() { private val CHANNEL_ID = "device_service_channel" override fun onCreate() { super.onCreate() // 创建通知渠道(Android 8.0+ 必需) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(CHANNEL_ID, "设备服务", NotificationManager.IMPORTANCE_DEFAULT) val manager = getSystemService(NotificationManager::class.java) manager.createNotificationChannel(channel) } } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val deviceName = intent?.getStringExtra("name") ?: "未知设备" // 构建通知,点击可打开应用 val launchIntent = packageManager.getLaunchIntentForPackage(packageName) val pendingIntent = PendingIntent.getActivity(this, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE) val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("设备连接中") .setContentText("当前连接设备:$deviceName") .setSmallIcon(R.drawable.ic_notification) .setContentIntent(pendingIntent) .build() // 启动前台服务 startForeground(1, notification) // 执行后台任务逻辑 handleBackgroundTask(intent) return START_STICKY } private fun handleBackgroundTask(intent: Intent?) { // 自定义后台任务处理 } override fun onBind(intent: Intent?): IBinder? = null }
- 在AndroidManifest.xml中声明权限与服务
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application ...> <service android:name=".DeviceForegroundService" /> </application>
方案二:静态BroadcastReceiver启动Flutter应用并传递参数
此方案无需应用提前运行,静态Receiver触发时直接启动Flutter应用,通过Intent传递参数,应用启动后自动执行处理逻辑,可选择不显示UI避免干扰用户。
- 注册静态BroadcastReceiver
<receiver android:name=".DeviceConnectedReceiver"> <intent-filter> <action android:name="android.bluetooth.device.action.ACL_CONNECTED" /> </intent-filter> </receiver>
- 实现静态Receiver
import android.bluetooth.BluetoothDevice import android.content.BroadcastReceiver import android.content.Context import android.content.Intent class DeviceConnectedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == BluetoothDevice.ACTION_ACL_CONNECTED) { val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) // 构建启动Flutter应用的Intent,携带参数 val launchIntent = context?.packageManager?.getLaunchIntentForPackage(context.packageName)?.apply { putExtra("from_acl_connected", true) putExtra("device_name", device?.name) putExtra("device_address", device?.address) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) } context?.startActivity(launchIntent) } } }
- Flutter端处理启动参数
import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; final MethodChannel _launchChannel = MethodChannel('com.example/launch_channel'); final MethodChannel _deviceChannel = MethodChannel('com.example/device_channel'); void main() async { WidgetsFlutterBinding.ensureInitialized(); // 获取启动参数 final Map<String, dynamic>? launchParams = await _launchChannel.invokeMethod('getLaunchParams'); if (launchParams?['from_acl_connected'] == true) { // 处理设备连接信息 final deviceName = launchParams?['device_name']; final deviceAddress = launchParams?['device_address']; // 启动前台服务 await _deviceChannel.invokeMethod('startForegroundService', { 'name': deviceName, 'address': deviceAddress }); // 将应用退到后台,不显示UI await _launchChannel.invokeMethod('moveAppToBackground'); return; } runApp(const MyApp()); }
- Android端提供参数获取与后台化方法
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) val launchChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example/launch_channel") launchChannel.setMethodCallHandler { call, result -> when (call.method) { "getLaunchParams" -> { val params = mapOf( "from_acl_connected" to intent.getBooleanExtra("from_acl_connected", false), "device_name" to intent.getStringExtra("device_name"), "device_address" to intent.getStringExtra("device_address") ) result.success(params) } "moveAppToBackground" -> { moveTaskToBack(true) result.success(null) } else -> result.notImplemented() } } } }
内容的提问来源于stack exchange,提问作者mspnr
相关产品推荐
相关产品推荐

