Вопрос или проблема
У меня есть приложение на 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. Различия между режимами
Убедитесь, что вы тестируете приложение в режимах отладки и выпуска на одном устройстве, чтобы сравнить поведение. Иногда настройка среды разработки или изменения в коду могут повлиять на результат.
Заключение
Проблема, которую вы описываете, скорее всего, связана с неправильным управлением состоянием приложения и кэшированием. Следует должным образом отслеживать и инициализировать данные, а также последовательно и адекватно обрабатывать состояние пользователя приложения. Рекомендуется использовать логи для отслеживания поведения приложения и оптимизации кода.
Если после выполнения всех рекомендаций проблема останется, попытайтесь сократить пример кода, чтобы более тщательно проанализировать конкретную часть, которая может вызывать проблемы, и поделитесь ею для дальнейшего анализа.