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

如何在应用未前台时从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:54:53