如何在应用未前台时从Android桌面组件调用Flutter方法
我们尝试在Kotlin中使用pigeon包,从Android桌面组件调用Flutter代码。组件通过AppWidgetProvider类中的PendingIntent实现屏幕触摸事件,进而通过pigeon生成的Kotlin/Flutter桥调用Flutter代码。
- 当Intent指向
MainActivity时功能正常,但会将应用激活至前台,不符合需求。 - 改用
Service作为Intent目标时,Kotlin端的pigeon桥能正常启动,但Flutter端对应的代码未被调用。
这是前后台问题吗?我们遗漏了什么?如何在不弹出应用的前提下完成该功能?
Pigeon 定义
import 'package:pigeon/pigeon.dart'; @FlutterApi() abstract class MessageFlutterApi { String flutterMethod(String operation, String? param); }
Flutter 代码
在main.dart层级实例化的类:
class WidgetCallback extends interaction.MessageFlutterApi { WidgetCallback._internal(); static final WidgetCallback _callback = WidgetCallback._internal(); factory WidgetCallback() { interaction.MessageFlutterApi.setUp(_callback); return _callback; } @override String flutterMethod(String operation, String? param) { return "Received $operation ($param)"; } }
Kotlin - Service
我们希望使用Service(非Activity)作为Intent目标。当前场景下onStartCommand被成功调用,Kotlin中生成的flutterMethod已执行,但Flutter端对应的方法未被调用:
class InteractionService:Service() { private lateinit var flutterEngine: FlutterEngine var caller: MessageFlutterApi? = null override fun onCreate() { super.onCreate() flutterEngine = FlutterEngine(this) caller = MessageFlutterApi(flutterEngine.dartExecutor.binaryMessenger) FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.action?.let { action -> caller?.flutterMethod(action, "", {}) } return START_STICKY } override fun onDestroy() { super.onDestroy() FlutterEngineCache.getInstance().remove("my_engine_id") } }
Kotlin - AppWidgetProvider
Intent指向InteractionService,PendingIntent关联对应Service:
class TinyTempWidget : AppWidgetProvider(){ override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { super.onUpdate(context, appWidgetManager, appWidgetIds) val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE for (appWidgetId in appWidgetIds) { val widgetData = HomeWidgetPlugin.getData(context) val intent = Intent(context, InteractionService::class.java) val pendingIntentUpdate = PendingIntent.getService(context,0,intent.setAction("ACTION_WIDGET_UPDATE"),flags) val thisView = RemoteViews(context.packageName, R.layout.tiny_temp_widget_1x1).apply { setOnClickPendingIntent(R.id.updated, pendingIntentUpdate) } val views = RemoteViews(thisView) appWidgetManager.updateAppWidget(appWidgetId, views) } }
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <activity android:name=".MainActivity" android:launchMode="singleInstance" </activity> <service android:name=".InteractionService" android:exported="false" android:stopWithTask="false" />
pubspec.yaml
dev_dependencies: pigeon: ^20.0.2
核心问题是Service中创建的Flutter Engine未执行Dart代码,导致Pigeon的Flutter端API未被注册。以下是关键修复步骤:
1. 启动Flutter Engine并执行Dart入口
在Service的onCreate方法中,创建FlutterEngine后必须调用run()相关逻辑启动Dart代码执行,否则Engine仅初始化但不会运行Flutter逻辑:
override fun onCreate() { super.onCreate() flutterEngine = FlutterEngine(this) // 执行默认Dart入口(main函数) flutterEngine.dartExecutor.executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) caller = MessageFlutterApi(flutterEngine.dartExecutor.binaryMessenger) FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine) }
2. 处理后台Service的系统限制
Android 12及以上对后台Service启动Flutter Engine有严格限制,需将Service设为前台Service避免被系统回收:
- 在
onCreate中添加前台通知逻辑:
override fun onCreate() { super.onCreate() // 创建前台通知(需提前在应用中创建对应通知通道) val notification = NotificationCompat.Builder(this, "WIDGET_INTERACTION_CHANNEL") .setContentTitle("组件交互服务") .setContentText("正在处理桌面组件请求") .setSmallIcon(R.drawable.ic_default_notification) .build() // 启动前台Service(适配Android版本) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) } else { startForeground(1, notification) } // 初始化Flutter Engine并执行Dart代码 flutterEngine = FlutterEngine(this) flutterEngine.dartExecutor.executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) caller = MessageFlutterApi(flutterEngine.dartExecutor.binaryMessenger) FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine) }
- 在
AndroidManifest.xml中更新Service配置并添加通知通道声明:
<application ...> <!-- 通知通道声明(Android 8.0+必填) --> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="WIDGET_INTERACTION_CHANNEL" /> <service android:name=".InteractionService" android:exported="false" android:stopWithTask="false" android:foregroundServiceType="dataSync" /> </application>
3. 确保Flutter端API注册时机
在Flutter的main()函数中尽早初始化WidgetCallback,保证Engine启动后立即注册Pigeon API:
void main() { // 优先初始化回调类 WidgetCallback(); runApp(const MyApp()); }
4. 优化Service生命周期
处理完请求后主动停止Service,避免长期驻留:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.action?.let { action -> caller?.flutterMethod(action, "", { result -> // 处理完回调后停止当前Service实例 stopSelf(startId) }) } return START_NOT_STICKY }
内容的提问来源于stack exchange,提问作者Mike Winter

