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

Flutter shared_preferences在Android谷歌应用商店生产环境失效

SharedPreferences在Flutter Google Play发布版中自动登录失效的排查方案

问题背景

使用shared_preferences: ^2.0.15实现Flutter应用自动登录功能,Android调试模式下一切正常,但发布至Google Play商店后自动登录失效。尝试过flutter build appbundle --release --no-shrink构建版本,问题依旧。

实现方式:通过main函数初始化单例类管理SharedPreferences实例,自动登录逻辑在StartupTask中执行。相关代码如下:

主函数代码

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await PrefsSingleton().init();
  // 后续逻辑...
}

SharedPreferences单例类

import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

class PrefsSingleton {
  static final PrefsSingleton _instance = PrefsSingleton._internal();

  late final SharedPreferences _prefs;

  factory PrefsSingleton() {
    return _instance;
  }

  PrefsSingleton._internal();

  Future<SharedPreferences> init() async => _prefs = await SharedPreferences.getInstance();

  Future<bool> setBool(String key, bool value) => _prefs.setBool(key, value);

  bool? getBool(String key) => _prefs.getBool(key);

  Future<bool> setString(String key, String value) => _prefs.setString(key, value);

  String? getString(String key) => _prefs.getString(key);

  Future<bool> setJsonEncodedString(String key, dynamic value) => _prefs.setString(key, json.encode(value));

  dynamic getJsonDecodedString(String key) {
    var jsonString = _prefs.getString(key);

    if (jsonString != null) {
      return jsonDecode(jsonString);
    }
    return null;
  }
}

自动登录逻辑

class AutoLogin implements StartupTask {
  @override
  Future<dynamic> runTask() async {
    var alreadyLoggedIn = PrefsSingleton().getBool("user.isLoggedIn") ?? false;

    if (alreadyLoggedIn) {
      var domain = PrefsSingleton().getString("user.domain");
      var pincode = PrefsSingleton().getString("user.pincode");

      await PrefsSingleton().setBool("user.isLoggedIn", false);

      if (domain != null && pincode != null) {
        var loggedIn = await ApiUserRepo().loginUser(domain, pincode);
        if (loggedIn) {
          await PluginSyncRepo().getSyncData();
        }
      }
    }
  }
}

环境信息

  • Flutter版本:3.0.5
  • Dart版本:2.17.6
  • Build Gradle版本:7.0.0

依赖包列表

cupertino_icons: ^1.0.2, flutter_riverpod: ^2.0.0-dev.9, device_info_plus: ^4.0.0, percent_indicator: ^4.2.2, pin_code_fields: ^7.4.0, flutter_spinkit: ^5.1.0, url_launcher: ^6.1.0, rest_client: ^2.1.4, crypto: ^3.0.2, catcher: ^0.7.0, shared_preferences: ^2.0.15, uuid: ^3.0.6, flutter_facebook_auth: ^4.3.4, intl: ^0.17.0, image_picker: ^0.8.5+3, connectivity_plus: ^2.3.2, json_annotation: ^4.5.0, reorderables: ^0.5.0, cached_network_image: ^3.2.1, string_validator: ^0.3.0, image: ^3.2.0, heic_to_jpg: ^0.2.0, path_provider: ^2.0.11, package_info_plus: ^1.4.2, camera: ^0.10.0+1, dropdown_button2: ^1.7.2

可能的原因及解决方案

1. R8/ProGuard混淆破坏SharedPreferences逻辑

即使使用--no-shrink,Google Play对App Bundle会默认启用R8混淆,可能导致SharedPreferences相关类被误删或篡改。需在android/app/proguard-rules.pro中添加保护规则:

-keep class io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin { *; }
-keep class androidx.preference.PreferenceManager { *; }
-keep class android.content.SharedPreferences { *; }
-keep class android.content.SharedPreferences$Editor { *; }

同时确保android/app/build.gradle的release配置正确:

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

2. 初始化时机不匹配

虽然main函数中await了PrefsSingleton.init(),但如果AutoLogin.runTask()的执行没有等待初始化完成,可能导致读取时_prefs尚未赋值。可在单例类中添加初始化状态检查,并在自动登录前确认:

// 在PrefsSingleton中添加
bool _isInitialized = false;

Future<SharedPreferences> init() async {
  _prefs = await SharedPreferences.getInstance();
  _isInitialized = true;
  return _prefs;
}

bool get isInitialized => _isInitialized;

// 在AutoLogin.runTask中先验证
@override
Future<dynamic> runTask() async {
  if (!PrefsSingleton().isInitialized) {
    await PrefsSingleton().init();
  }
  // 原有逻辑...
}

3. Flutter Riverpod预览版潜在问题

flutter_riverpod 2.0.0-dev.9为预览版,可能存在异步初始化冲突。建议升级至稳定版(当前最新稳定版为2.3.0+),或确保SharedPreferences初始化完成后再初始化Riverpod的ProviderScope。

4. 验证SharedPreferences读写状态

在发布版中添加日志捕获(利用已集成的catcher),确认登录成功后数据是否正常写入:

// 登录成功后添加
bool setLoginStatus = await PrefsSingleton().setBool("user.isLoggedIn", true);
bool setDomain = await PrefsSingleton().setString("user.domain", domain);
bool setPincode = await PrefsSingleton().setString("user.pincode", pincode);

// 用catcher记录日志
Catcher.reportCheckedError(
  null,
  StackTrace.current,
  message: "SP写入结果: isLoggedIn=$setLoginStatus, domain=$setDomain, pincode=$setPincode"
);

同时在自动登录逻辑中添加读取日志,确认是否能获取到数据。

5. 测试发布版签名包

如果启用了Google Play App Signing,本地调试签名与发布签名不同,可尝试用发布版签名密钥打包APK安装到设备上测试,确认是否能正常读取SharedPreferences数据。


内容的提问来源于stack exchange,提问作者anton-definit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:09:26