Различия между отладочной и релизной версией приложения Flutter в Google Console

Вопрос или проблема

У меня есть приложение на Flutter, которое, когда я запускаю его на своем физическом устройстве Android в режиме отладки, работает гладко, как и ожидалось, даже в режиме релиза. Проблема возникает, когда я собираю appbundle для релиза в Google Console. Приложение ведет себя странно, поскольку при свежей установке должно отображаться руководство (экраны пропуска). В выпущенном приложении в Google Console эти экраны не отображаются, оно сразу переходит на экран входа. То же самое происходит с API.
Я хочу добавить, что приложение версии Google Console начинает нормально работать только тогда, когда я зайду в информацию о приложении на своем устройстве и очищу данные и кэш, после чего все начинает работать как задумано.

Вот мой код:

void main() async {
  // Убедитесь, что Flutter инициализирован перед доступом к SharedPreferences
  WidgetsFlutterBinding.ensureInitialized();

  // Ограничьте приложение альбомной ориентацией
  await SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);

  await Styles.loadAppConfig();

  // Запустите приложение с выбранной домашней страницей, обернутой в MaterialApp
  runApp(const MyApp(
    homePage: SplashScreen(),
  ));
}

SplashScreen внутри initState после экрана запуска:

Timer(const Duration(seconds: 2), () async {
        if (mounted) {
          // Перейдите к главному экрану через 2 секунды
          // Прочитайте флаг из shared preferences
          SharedPreferences prefs = await SharedPreferences.getInstance();

          // Проверьте, новый ли это пользователь; если он null, установите его в true
          bool isNewUser = prefs.getBool('isNewUser') ?? true;
          bool isLoggedIn = prefs.getBool('isUserLoggedIn') ?? false;
          bool isLoggedInMemberStored = await prefs.setBool('isLoggedInMemberStored', false);
          bool isSessionExpired = prefs.getBool('isSessionExpired') ?? false;

          // Определите главную страницу в зависимости от статуса пользователя
          Widget homePage = const LoginPage(); // только инициализация домашней страницы

          // Если это первый запуск приложения
          if (isNewUser) {
            // Установите логин как false в shared preferences (подтверждение)
            prefs.setBool('isUserLoggedIn', false);

            // Отобразите руководство, если пользователь не вошел в систему
            if (!isLoggedIn) {
              homePage = WTpageController();
            }
          } else {
            // Перенаправьте пользователя на вход, если он не новый
            if (isLoggedIn) {
              SessionManager().startSession();
              homePage = NavbarController();
            } else {
              homePage = LoginPage();
            }
          }

          runApp(MyApp(
            homePage: homePage,
          ));
        }
      });

build.gradle:

plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def keystorePropertiesFile = rootProject.file('key.properties')
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

def flutterVersionCode="36" // Обновите код версии на '2'
def flutterVersionName="1.0.36" // Обновите имя версии на '1.0.1'

android {
    namespace "com.****.****"
    compileSdk 34
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget="1.8"
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        applicationId "com.****.****"
        minSdkVersion 21
        targetSdkVersion 34
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        multiDexEnabled true
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            // По умолчанию используется конфигурация подписи отладки для сборок релиза
            signingConfig signingConfigs.release
            minifyEnabled false
            shrinkResources false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

flutter {
    source '../..'
}

dependencies {}

Я не уверен, но думаю, что моя логика правильная, потому что она работает как задумано, когда я запускаю приложение непосредственно на своем мобильном устройстве.
Я пытался внести изменения в типы сборок релиза:

buildTypes {
        release {
            // По умолчанию используется конфигурация подписи отладки для сборок релиза
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

Я добавил функцию и вызвал её после if (isNewUser) {, где она очищает данные и кэш приложения, потому что оно работает нормально, когда я вручную удаляю кэш и данные приложения:

Future<void> _clearCacheAndData() async {
    await _clearCache();
    await _clearAppData();
    await _clearSharedPreferences();
  }

  Future<void> _clearCache() async {
    try {
      Directory tempDir = await getTemporaryDirectory();
      Directory cacheDir = await getApplicationCacheDirectory();
      if (tempDir.existsSync()) {
        tempDir.deleteSync(recursive: true);
        print("очистка tempDir");
      }
      if (cacheDir.existsSync()) {
        cacheDir.deleteSync(recursive: true);
        print("очистка cacheDir");
      }
    } catch (e) {
      print("Ошибка при очистке кэша: $e");
    }
  }

  Future<void> _clearAppData() async {
    try {
      Directory appDir = await getApplicationDocumentsDirectory();
      if (appDir.existsSync()) {
        appDir.deleteSync(recursive: true);
        print("очистка appDir");
      }
    } catch (e) {
      print("Ошибка при очистке данных приложения: $e");
    }
  }

  Future<void> _clearSharedPreferences() async {
    try {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      await prefs.clear();
    } catch (e) {
      print("Ошибка при очистке общих предпочтений: $e");
    }
  }

Может ли это быть связано с Google Console?
Пожалуйста, помогите мне, и если нужно, я могу предоставить больше блоков кода или объяснить более подробно.

Ответ или решение

Проблема, с которой вы столкнулись, связана с тем, что поведение вашего приложения в режиме отладки и в режиме выпуска (release) различается при установке из Google Play Console. Это может быть вызвано несколькими факторами, включая кэширование данных приложения, неправильное управление SharedPreferences или особенности сборки вашего приложения.

Вот возможные причины проблемы и рекомендации по их решению:

1. Проблемы с SharedPreferences

Похоже, что ваше приложение не правильно считывает значения из SharedPreferences при первом запуске. Этим может быть вызвано некорректное сохранение состояния пользователя между запусками приложения.

Убедитесь, что вы правильно инициализируете SharedPreferences на первом запуске, и проверяйте их значения. Возможно, имеет смысл добавить дополнительную логику для обработки ситуаций, когда значения отсутствуют.

2. Кэширование и данные приложения

Если ваше приложение работает корректно только после удаления кэша и данных, это может указывать на проблему с кэшированием. Чтобы устранить эту проблему, вы можете выполнить следующее:

  • Убедитесь, что значения в SharedPreferences и других хранилищах данных инициализируются только один раз. Если они уже установлены, приложение не должно их перезаписывать без необходимости.
  • Рассмотрите возможность добавления логов, чтобы отслеживать, как и когда данные записываются и считываются. Это может дать ясность в том, как происходит инициализация вашего приложения.

3. Проверьте логи

Используйте логи для отладки вашего приложения в режиме выпуска. Как правило, логи выполнения можно получить через Android Studio или при помощи adb logcat. Это поможет понять, где именно происходит сбой.

4. Изменения в build.gradle

Ваши изменения в файле build.gradle относительно настроек minify и shrinkResources могут влиять на поведение приложения. Попробуйте вернуть настройки:

buildTypes {
    release {
        signingConfig signingConfigs.release
        minifyEnabled false
        shrinkResources false
    }
}

Так как при сжатии ресурсов и минификации кода, используются правила ProGuard, что может вызвать непредвиденные ошибки в сборке. Убедитесь, что ваши правила ProGuard не удаляют необходимые классы и методы.

5. Очистка кэша приложения

Хотя вы добавили метод для удаления кэша, он не должен вызываться при каждом запуске приложения. Это должно быть в крайнем случае, чтобы избежать негативного опыта пользователя. Попробуйте обнаружить, какие данные вызывают проблемы, и цельно их очищать.

Future<void> _clearCache() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.clear(); // Удаляем все данные (проверяйте это действие, чтобы не потерять важные значения)
}

6. Различия между режимами

Убедитесь, что вы тестируете приложение в режимах отладки и выпуска на одном устройстве, чтобы сравнить поведение. Иногда настройка среды разработки или изменения в коду могут повлиять на результат.

Заключение

Проблема, которую вы описываете, скорее всего, связана с неправильным управлением состоянием приложения и кэшированием. Следует должным образом отслеживать и инициализировать данные, а также последовательно и адекватно обрабатывать состояние пользователя приложения. Рекомендуется использовать логи для отслеживания поведения приложения и оптимизации кода.

Если после выполнения всех рекомендаций проблема останется, попытайтесь сократить пример кода, чтобы более тщательно проанализировать конкретную часть, которая может вызывать проблемы, и поделитесь ею для дальнейшего анализа.

Оцените материал
Добавить комментарий

Капча загружается...