如何在应用终止时运行Dart代码(Android桌面组件场景)
解决方案:让Android桌面组件在应用终止时仍能执行Dart API调用
问题根源
当前代码依赖FlutterEngineCache中缓存的引擎实例,当应用被终止后,缓存的引擎会被销毁,导致flutterEngine为null,无法触发Dart侧的API调用。要实现应用未运行时也能工作,需要手动初始化FlutterEngine,而非依赖缓存的实例。
修改步骤
1. 修改Android端代码
核心是用JobIntentService执行Flutter引擎初始化和方法调用(规避Android 8.0+后台启动限制,无需第三方插件),替代原有的缓存引擎依赖逻辑。
首先创建FlutterIntentService类:
import android.content.Context; import android.content.Intent; import androidx.core.app.JobIntentService; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.plugin.common.MethodChannel; public class FlutterIntentService extends JobIntentService { private static final int JOB_ID = 1001; private static final String CHANNEL_NAME = "my_channel123"; private FlutterEngine flutterEngine; public static void enqueueWork(Context context, Intent work) { enqueueWork(context, FlutterIntentService.class, JOB_ID, work); } @Override protected void onHandleWork(Intent intent) { // 初始化FlutterEngine flutterEngine = new FlutterEngine(this); flutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); // 等待引擎初始化完成 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 设置MethodChannel并处理回调 MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL_NAME); methodChannel.setMethodCallHandler((call, result) -> { if (call.method.equals("InvokedWidget")) { String tempValue = call.argument("message"); // 更新所有桌面组件 NewAppWidget.valueToBeDisplayed = tempValue; AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, NewAppWidget.class)); for (int appWidgetId : appWidgetIds) { NewAppWidget.updateAppWidget(this, appWidgetManager, appWidgetId); } result.success("温度已更新"); // 销毁引擎释放资源 flutterEngine.destroy(); } }); // 调用Dart侧API获取数据 methodChannel.invokeMethod("requestLatestData", null); } @Override public void onDestroy() { super.onDestroy(); if (flutterEngine != null) { flutterEngine.destroy(); } } }
修改NewAppWidget中的callFlutterMethod方法,改为启动服务:
private static void callFlutterMethod(Context context) { System.out.print("触发Dart方法调用"); Intent intent = new Intent(context, FlutterIntentService.class); FlutterIntentService.enqueueWork(context, intent); }
在AndroidManifest.xml中注册服务:
<service android:name=".FlutterIntentService" android:permission="android.permission.BIND_JOB_SERVICE"/>
2. 修改Dart端代码
将API调用和MethodChannel监听逻辑与页面状态分离,确保应用启动时(即使未打开页面)也能响应Android端调用:
import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { // 全局初始化MethodChannel监听 const MethodChannel methodChannel = MethodChannel('my_channel123'); methodChannel.setMethodCallHandler(methodHandler); runApp(const MyApp()); } // 独立处理Android端方法调用 Future<void> methodHandler(MethodCall call) async { switch (call.method) { case "requestLatestData": await fetchAndSendTemperature(); break; default: print('未匹配到方法: ${call.method}'); } } // 独立的API调用与数据回传逻辑 Future<void> fetchAndSendTemperature() async { const MethodChannel methodChannel = MethodChannel('my_channel123'); try { final response = await http.get(Uri.parse('http://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m')); if (response.statusCode == 200) { final json = jsonDecode(response.body); final List<dynamic> temperatures = json['hourly']['temperature_2m']; // 取最新温度值(可根据需求调整) String latestTemp = temperatures.first.toString(); await methodChannel.invokeMethod("InvokedWidget", {'message': latestTemp}); } } catch (e) { print('API调用失败: $e'); } } // 原有UI代码保持不变 class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { List<dynamic> lst = []; int num = 0; @override void initState() { super.initState(); fetchTemperatureData(); } Future<void> fetchTemperatureData() async { final response = await http.get(Uri.parse('http://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m')); if (response.statusCode == 200) { final json = jsonDecode(response.body); setState(() { lst = json['hourly']['temperature_2m']; }); } } void updateData() { setState(() { num = (num + 1) % lst.length; }); } @override Widget build(BuildContext context) { return lst.isEmpty ? Container(color: Colors.blue) : Scaffold( body: Center( child: Container( color: Colors.black, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( "当前温度", style: TextStyle(color: Colors.white, fontSize: 48, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Text( lst[num].toString(), style: const TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold), ), const SizedBox(height: 100), ElevatedButton( onPressed: updateData, child: const Text( '更新温度', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), ) ], ), ), ), ); } }
关键说明
- 手动初始化FlutterEngine:每次调用Dart方法时创建新引擎,完成后销毁,避免内存泄漏,不再依赖应用缓存的引擎实例。
- JobIntentService:在后台执行任务,规避Android 8.0+的后台启动限制,确保应用终止时仍能触发Dart代码。
- 独立Dart逻辑:将API调用与页面状态解耦,确保应用启动后(即使未打开页面)也能响应Android端的方法调用。
内容的提问来源于stack exchange,提问作者VENKAT RUTHVIK SAI KONDA
相关产品推荐
相关产品推荐

