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

挂载USB设备重启后Flutter应用最近任务列表重复显示的解决办法

解决方案:避免USB触发的Flutter应用多实例问题

针对你遇到的USB设备挂载导致最近任务列表出现多实例的问题,以下是几个可行的实现方案,均可以通过Flutter方法通道结合Android原生代码完成:

一、原生层提前检测实例并跳转(最直接方案)

在MainActivity初始化前,主动检测是否已有本应用的运行实例,若存在则切换到该实例并终止当前进程:

  1. 修改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)
}
  1. 补充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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:24:54