Flutter应用关闭前如何保存数据?需Kotlin与Swift原生示例
解决Flutter应用被杀死前保存状态的问题
问题分析
当用户通过安卓应用概览上滑关闭、iOS应用切换界面划掉应用时,Flutter的detached生命周期状态、Android的onDestroy()方法大概率不会触发——因为系统可能直接终止进程,不会执行这些常规生命周期回调。要实现"被杀死前保存最后访问页面和时间"的需求,需要依赖原生平台的后台状态监听机制,在应用进入后台时完成状态保存(这是系统一定会触发的回调,同时覆盖进程被杀死的场景)。
Android端实现(Kotlin)
安卓可以通过ProcessLifecycleOwner监听整个应用的生命周期,在应用退到后台时执行保存操作,同时可结合内存回收回调补充覆盖极端场景。
1. 添加依赖
在app/build.gradle的dependencies块中添加:
implementation "androidx.lifecycle:lifecycle-process:2.6.2"
2. 实现应用生命周期监听
自定义Application类并注册监听:
import android.app.Application import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.dart.DartExecutor import io.flutter.plugin.common.MethodChannel import java.text.SimpleDateFormat import java.util.* class MyApp : Application(), LifecycleObserver { private val CHANNEL = "app_state_channel" private var currentPage = "home_page" override fun onCreate() { super.onCreate() ProcessLifecycleOwner.get().lifecycle.addObserver(this) // 初始化MethodChannel,接收Flutter传递的当前页面信息 val flutterEngine = FlutterEngine(this) flutterEngine.dartExecutor.executeDartEntrypoint( FlutterEngine.DartEntrypoint.createDefault() ) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "setCurrentPage") { currentPage = call.argument<String>("page") ?: "home_page" result.success(null) } else { result.notImplemented() } } } // 应用退到后台时触发 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onAppBackgrounded() { saveAppState() } // 系统内存不足、即将杀死进程时触发(补充场景) override fun onTrimMemory(level: Int) { super.onTrimMemory(level) if (level == TRIM_MEMORY_COMPLETE) { saveAppState() } } private fun saveAppState() { val closeTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()) // 保存到SharedPreferences(也可替换为数据库操作,注意轻量化) val sharedPref = getSharedPreferences("AppState", MODE_PRIVATE) with(sharedPref.edit()) { putString("last_page", currentPage) putString("close_time", closeTime) apply() // 用apply()避免阻塞主线程 } } }
别忘了在AndroidManifest.xml中指定自定义Application:
<application android:name=".MyApp" ...> </application>
iOS端实现(Swift)
iOS在应用进入后台时会触发sceneDidEnterBackground(iOS13+)或applicationDidEnterBackground(iOS12及以下)回调,可在此完成状态保存。
iOS13+ 实现(SceneDelegate)
import UIKit import Flutter class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? private let CHANNEL = "app_state_channel" private var currentPage = "home_page" func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } // 初始化Flutter引擎与MethodChannel let flutterEngine = FlutterEngine(name: "MyFlutterEngine") flutterEngine.run() GeneratedPluginRegistrant.register(with: flutterEngine) let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: flutterEngine.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } if call.method == "setCurrentPage" { if let args = call.arguments as? [String: String], let pageName = args["page"] { self.currentPage = pageName } result.success(nil) } else { result.notImplemented() } } window = UIWindow(windowScene: windowScene) window?.rootViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil) window?.makeKeyAndVisible() } // 应用退到后台时触发 func sceneDidEnterBackground(_ scene: UIScene) { saveAppState() } private func saveAppState() { let closeTime = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .medium) // 保存到UserDefaults(也可替换为数据库操作) UserDefaults.standard.set(currentPage, forKey: "last_page") UserDefaults.standard.set(closeTime, forKey: "close_time") UserDefaults.standard.synchronize() } }
iOS12及以下兼容(AppDelegate)
import UIKit import Flutter @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let CHANNEL = "app_state_channel" private var currentPage = "home_page" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let flutterEngine = FlutterEngine(name: "MyFlutterEngine") flutterEngine.run() GeneratedPluginRegistrant.register(with: flutterEngine) let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: flutterEngine.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } if call.method == "setCurrentPage" { if let args = call.arguments as? [String: String], let pageName = args["page"] { self.currentPage = pageName } result.success(nil) } else { result.notImplemented() } } window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil) window?.makeKeyAndVisible() return true } func applicationDidEnterBackground(_ application: UIApplication) { saveAppState() } private func saveAppState() { let closeTime = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .medium) UserDefaults.standard.set(currentPage, forKey: "last_page") UserDefaults.standard.set(closeTime, forKey: "close_time") UserDefaults.standard.synchronize() } }
Flutter端配合代码
通过MethodChannel向原生传递当前页面信息,在每个页面的initState中调用:
import 'package:flutter/services.dart'; class PageStateManager { static const MethodChannel _channel = MethodChannel('app_state_channel'); static Future<void> sendCurrentPage(String pageName) async { try { await _channel.invokeMethod('setCurrentPage', {'page': pageName}); } on PlatformException catch (e) { print("Failed to send page info: ${e.message}"); } } } // 示例页面调用 class HomePage extends StatefulWidget { @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { @override void initState() { super.initState(); PageStateManager.sendCurrentPage('home_page'); } @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text("Home"))); } }
关键注意事项
- 不要依赖
detached、onDestroy()或applicationWillTerminate,这些回调在进程被强制杀死时大概率不会执行。 - 保存操作必须轻量化,避免耗时IO操作,否则可能被系统终止或引发ANR(安卓)。
- 优先在应用进入后台时保存状态,既覆盖进程被杀死的场景,也能在正常退后台时保存最新状态,提升用户恢复体验。
内容的提问来源于stack exchange,提问作者Ibne
相关产品推荐
相关产品推荐

