挂载USB设备重启后Flutter应用最近任务列表重复显示的解决办法
解决方案:避免USB触发的Flutter应用多实例问题
针对你遇到的USB设备挂载导致最近任务列表出现多实例的问题,以下是几个可行的实现方案,均可以通过Flutter方法通道结合Android原生代码完成:
一、原生层提前检测实例并跳转(最直接方案)
在MainActivity初始化前,主动检测是否已有本应用的运行实例,若存在则切换到该实例并终止当前进程:
- 修改MainActivity的
onCreate方法:
override fun onCreate(savedInstanceState: Bundle?) { // 优先检测已有运行实例 val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val runningTasks = activityManager.getRunningTasks(Int.MAX_VALUE) for (task in runningTasks) { // 找到同包名的非当前任务实例 if (task.baseActivity?.packageName == packageName && task.id != taskId) { // 切换到已有实例的任务栈 val intent = Intent(this, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(intent) // 终止当前实例 finish() return } } // 原有Flutter初始化逻辑 super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) }
- 补充
onNewIntent处理USB触发的Intent:
override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) if (intent?.action == UsbManager.ACTION_USB_DEVICE_ATTACHED) { // 在这里通过MethodChannel通知Flutter层处理USB挂载逻辑 // 避免因Intent重复创建实例 } }
注:
getRunningTasks在API 21后标记为废弃,但大部分设备仍可正常工作;若需兼容更高版本,可替换为UsageStatsManager,但需额外申请android.permission.PACKAGE_USAGE_STATS权限并引导用户开启应用使用权限。
二、调整Manifest配置强化单实例特性
保持launchMode="singleTask"的同时,添加额外配置避免系统重启后任务栈异常:
修改AndroidManifest.xml中MainActivity的配置:
<activity android:name=".MainActivity" android:launchMode="singleTask" android:alwaysRetainTaskState="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device_filter"/> </activity>
android:alwaysRetainTaskState="true"可防止系统在内存不足时重建任务栈,减少异常实例生成的概率。
三、Flutter层主动检测并触发切换
通过MethodChannel在Flutter初始化阶段调用原生接口检测实例,若存在则触发切换并退出当前实例:
Flutter端代码
import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; class InstanceChecker { static const MethodChannel _channel = MethodChannel('instance_checker'); static Future<bool> hasRunningInstance() async { try { return await _channel.invokeMethod('hasRunningInstance'); } on PlatformException catch (_) { return false; } } static Future<void> switchToExistingInstance() async { try { await _channel.invokeMethod('switchToExistingInstance'); } on PlatformException catch (_) {} } } void main() async { WidgetsFlutterBinding.ensureInitialized(); bool hasInstance = await InstanceChecker.hasRunningInstance(); if (hasInstance) { await InstanceChecker.switchToExistingInstance(); // 退出当前Flutter实例 SystemNavigator.pop(animated: false); return; } runApp(MyApp()); }
Android原生端MethodChannel实现
class MainActivity : FlutterActivity() { private val CHANNEL = "instance_checker" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "hasRunningInstance" -> { result.success(checkForRunningInstance()) } "switchToExistingInstance" -> { switchToExistingInstance() result.success(null) } else -> result.notImplemented() } } } private fun checkForRunningInstance(): Boolean { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val runningTasks = activityManager.getRunningTasks(Int.MAX_VALUE) return runningTasks.any { task -> task.baseActivity?.packageName == packageName && task.id != taskId } } private fun switchToExistingInstance() { val intent = Intent(this, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(intent) finish() } }
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

