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

如何从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层。

  1. 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) {
  // 自定义设备连接后的处理逻辑
}
  1. 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)
    }
}
  1. 实现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
}
  1. 在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避免干扰用户。

  1. 注册静态BroadcastReceiver
<receiver android:name=".DeviceConnectedReceiver">
    <intent-filter>
        <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
    </intent-filter>
</receiver>
  1. 实现静态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)
        }
    }
}
  1. 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());
}
  1. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:57:34