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

如何在应用终止时运行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:18:12