Flutter插件iOS报错:[Security] 方法不应在主线程调用
问题:Flutter集成Mastercard Connect SDK时主线程安全警告
错误日志
2022-12-21 23:52:39.678730+0530 Runner[36839:1696429] [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.
现有代码
import Flutter import UIKit import Connect public class SwiftFinicityPlugin: NSObject, FlutterPlugin { var flutterResult: FlutterResult! var connectViewController: ConnectViewController! var connectNavController: UINavigationController! public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "finicity", binaryMessenger: registrar.messenger()) let instance = SwiftFinicityPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { flutterResult = result; guard let args = call.arguments as? [String : Any] else {return} let connectUrl = args["connectUrl"] as! String let config = ConnectViewConfig(connectUrl: connectUrl, loaded: self.connectLoaded, done: self.connectDone, cancel: self.connectCancelled, error: self.connectError, route: self.connectRoute, userEvent: self.connectUserEvent) flutterResult("iOS " + UIDevice.current.systemVersion) self.connectViewController = ConnectViewController() self.connectViewController.load(config: config) } func connectLoaded() { self.connectNavController = UINavigationController(rootViewController: self.connectViewController) self.connectNavController.modalPresentationStyle = .fullScreen self.present(self.connectNavController, animated: false) } func connectDone(_ data: NSDictionary?) { flutterResult("iOS " + UIDevice.current.systemVersion) } func connectCancelled() { // Connect flow exited prematurely } func connectError(_ data: NSDictionary?) { // Error encountered in Connect flow } func connectRoute(_data: NSDictionary?) { // Connect route changed } func connectUserEvent(_ data: NSDictionary?) { // Connect user event fired in response to user action } }
问题分析
- 主线程阻塞风险:
handle方法是Flutter MethodChannel的回调,默认运行在主线程。直接在主线程调用connectViewController.load(config:),而该方法内部可能包含耗时的安全校验(如证书验证、加密操作),触发了系统的主线程安全警告。 - Flutter Result重复调用:
handle方法中先调用了一次flutterResult返回系统版本,后续connectDone又再次调用,这会导致Flutter侧抛出"Result already called"的错误。 - UI操作线程问题:虽然
connectLoaded中的present操作本身需要在主线程,但前置的load操作不应占用主线程资源。
解决方案
修改代码,将耗时的SDK加载操作移至后台队列,确保UI操作回到主线程,同时修复Result重复调用的问题:
import Flutter import UIKit import Connect public class SwiftFinicityPlugin: NSObject, FlutterPlugin { private var flutterResult: FlutterResult? private var connectViewController: ConnectViewController? private var connectNavController: UINavigationController? // 获取当前应用的根控制器,用于present弹窗 private var rootViewController: UIViewController? { UIApplication.shared.keyWindow?.rootViewController } public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "finicity", binaryMessenger: registrar.messenger()) let instance = SwiftFinicityPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { flutterResult = result guard let args = call.arguments as? [String: Any], let connectUrl = args["connectUrl"] as? String else { result(FlutterError(code: "INVALID_ARGS", message: "缺少必要参数connectUrl", details: nil)) return } let config = ConnectViewConfig( connectUrl: connectUrl, loaded: connectLoaded, done: connectDone, cancel: connectCancelled, error: connectError, route: connectRoute, userEvent: connectUserEvent ) // 将SDK加载操作移至后台队列,避免阻塞主线程 DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } self.connectViewController = ConnectViewController() self.connectViewController?.load(config: config) } } private func connectLoaded() { // UI操作必须回到主线程 DispatchQueue.main.async { [weak self] in guard let self = self, let vc = self.connectViewController else { return } self.connectNavController = UINavigationController(rootViewController: vc) self.connectNavController?.modalPresentationStyle = .fullScreen self.rootViewController?.present(self.connectNavController!, animated: false) } } private func connectDone(_ data: NSDictionary?) { // 仅在完成时返回结果,避免重复调用 flutterResult?(["status": "done", "data": data]) flutterResult = nil } private func connectCancelled() { flutterResult?(["status": "cancelled"]) flutterResult = nil } private func connectError(_ data: NSDictionary?) { flutterResult?(FlutterError(code: "CONNECT_ERROR", message: "Connect流程出错", details: data)) flutterResult = nil } private func connectRoute(_ data: NSDictionary?) { // 可通过EventChannel向Flutter发送路由变更事件,这里暂留空 } private func connectUserEvent(_ data: NSDictionary?) { // 可通过EventChannel向Flutter发送用户事件,这里暂留空 } }
关键修改点
- 使用
DispatchQueue.global().async将connectViewController.load移至后台线程执行,规避主线程安全警告。 - 所有UI操作(如present控制器)通过
DispatchQueue.main.async切回主线程,符合iOS UI操作规范。 - 修复
flutterResult重复调用问题,仅在Connect流程的完成、取消、错误状态下调用一次,并及时置空避免后续误调用。 - 添加参数校验,当缺少必要参数时返回明确的Flutter错误。
- 使用弱引用
[weak self]避免循环引用,提升内存安全性。
内容的提问来源于stack exchange,提问作者Gvs Akhil
相关产品推荐
相关产品推荐

