Flutter升级器报错:PlayStoreSearchAPI.lookupById返回null的解决方法
Flutter启动时Crashlytics报错:PlayStoreSearchAPI.lookupById返回null的修复方案
问题描述
每次启动Flutter应用都会触发Crashlytics错误,报错位置在app_update_controller.dart,错误原因是PlayStoreSearchAPI.lookupById返回null。
环境信息
- Flutter (Channel stable, 3.19.1, on macOS 13.5.2 22G91 darwin-arm64, locale ru-AE)
- Android toolchain - develop for Android devices (Android SDK version 34.0.0)
- Xcode - develop for iOS and macOS (Xcode 14.3.1)
- Chrome - develop for the web
- Android Studio (version 2023.1)
- VS Code (version 1.87.0)
- Connected device (3 available)
- Network resources
相关代码(AppUpdateController)
class AppUpdateController extends GetxController { final _logger = Get.find<ILogger>(); String installedVersion = ''; String packageName = ''; String appName = ''; String storeVersion = ''; String itunesStoreUrl = ''; final PlayStoreSearchAPI _playStoreSearchAPI = PlayStoreSearchAPI(); final ITunesSearchAPI _iTunesSearchAPI = ITunesSearchAPI(); late final FirebaseRemoteConfig remoteConfig; static const Duration remindLaterDuration = Duration(days: 1); static AppUpdateController get find => Get.find(); Future<void> init(FirebaseRemoteConfig remoteConfig) async { this.remoteConfig = remoteConfig; await _checkAppUpdate(true); } void getPackageInfo() => _getPackageInfo(); Future<void> _getPackageInfo() async { _logger.verbose('Getting package info...'); var packageInfo = await PackageInfo.fromPlatform(); installedVersion = packageInfo.version; packageName = packageInfo.packageName; appName = packageInfo.appName; } Future<void> _checkAppUpdate(bool showAlert) async { await _getPackageInfo(); if (Platform.isAndroid) { final response = await _playStoreSearchAPI.lookupById( packageName, country: Get.deviceLocale?.countryCode, language: Get.deviceLocale?.languageCode, ); if (response == null) { _logger.error('Cant fetch android store version info'); return; } final String? version = _playStoreSearchAPI.version(response); if (version == null) { _logger.error('Failed to parse android store version'); return; } storeVersion = version; } else if (Platform.isIOS) { final response = await _iTunesSearchAPI.lookupByBundleId(packageName, country: Get.deviceLocale?.countryCode); if (response == null) { _logger.error('Cant fetch ios store version info'); return; } final String? version = _iTunesSearchAPI.version(response); if (version == null) { _logger.error('Failed to parse ios store version'); return; } storeVersion = version; itunesStoreUrl = _iTunesSearchAPI.trackViewUrl(response) ?? ''; } else { _logger .info('Updater: Platform ${Platform.operatingSystem} not supported.'); return; } final Version parsedCurrentVersion = Version.parse(installedVersion); final Version parsedStoreVersion = Version.parse(storeVersion); _logger.info( 'Got version info from store. storeVersion: $parsedStoreVersion, currentVersion: $parsedCurrentVersion'); if (parsedStoreVersion > parsedCurrentVersion) { _logger.info('Store version is newer. Requesting update...'); final String minVersion = remoteConfig.getString(Defaults.remoteConfigMinVersionKey); final Version parsedMinVersion = minVersion.isEmpty ? Version(0, 0, 0) : Version.parse(minVersion); late final bool updateRequired; if (parsedMinVersion > parsedCurrentVersion) { updateRequired = true; } else { updateRequired = false; } final DateTime lastUpdateRequestedString = await _getLastUpdateRequestTime(); final DateTime now = DateTime.now(); if (!updateRequired && now.difference(lastUpdateRequestedString).inSeconds < remindLaterDuration.inSeconds) { _logger.info( 'User suspended update by pressing "install later" button before'); return; } RouteManagement.goToAppUpdateView(); } } Future<DateTime> _getLastUpdateRequestTime() async { final String? lastUpdateRequestedString = await HiveService.get<String>( HiveBoxNames.lashUpdateRequested, Defaults.lastUpdateRequestedTimeKey); if (lastUpdateRequestedString == null) return DateTime(0); return DateTime.parse(lastUpdateRequestedString); } Future<void> _setLastUpdateRequestTime(DateTime date) { return HiveService.put(HiveBoxNames.lashUpdateRequested, Defaults.lastUpdateRequestedTimeKey, date.toIso8601String()); } sendLaterLogs() { _logger.info('User pressed later update'); } sendUpdateLogs() { _logger.info('User pressed update'); } openStore() { if (Platform.isAndroid || Platform.isIOS) { final url = Uri.parse( Platform.isAndroid ? AppUrls.appPackageAndroidUrlFormat + packageName : itunesStoreUrl, ); launchUrl( url, mode: LaunchMode.externalApplication, ); } } }
修复方案
1. 增加网络请求重试机制
lookupById返回null大概率是网络请求失败导致的,给请求加重试逻辑,避免单次失败直接触发错误:
// 修改Android分支的请求逻辑 final response = await _retryPlayStoreLookup(packageName);
添加重试方法:
Future<dynamic> _retryPlayStoreLookup(String packageName, {int retries = 2}) async { for (int i = 0; i <= retries; i++) { try { final result = await _playStoreSearchAPI.lookupById( packageName, country: Get.deviceLocale?.countryCode, language: Get.deviceLocale?.languageCode, ); if (result != null) return result; } catch (e) { _logger.warning('PlayStore lookup attempt ${i+1} failed: $e'); if (i < retries) await Future.delayed(const Duration(milliseconds: 500)); } } return null; }
2. 处理设备Locale为null的情况
如果Get.deviceLocale返回null,传递country和language参数可能导致API请求异常,给默认值:
final response = await _playStoreSearchAPI.lookupById( packageName, country: Get.deviceLocale?.countryCode ?? 'US', language: Get.deviceLocale?.languageCode ?? 'en', );
3. 捕获API请求异常
原代码未捕获lookupById可能抛出的异常,直接导致null返回,添加try-catch包裹请求:
if (Platform.isAndroid) { try { final response = await _playStoreSearchAPI.lookupById( packageName, country: Get.deviceLocale?.countryCode ?? 'US', language: Get.deviceLocale?.languageCode ?? 'en', ); if (response == null) { _logger.error('Cant fetch android store version info'); return; } // 后续版本解析逻辑... } catch (e, stackTrace) { _logger.error('PlayStore lookup failed with exception: $e', stackTrace); return; } }
4. 延迟检查更新时机
避免在启动时立即执行更新检查,等待应用完全初始化后再执行,减少网络请求冲突:
修改init方法:
Future<void> init(FirebaseRemoteConfig remoteConfig) async { this.remoteConfig = remoteConfig; // 延迟1秒执行更新检查 await Future.delayed(const Duration(seconds: 1)); await _checkAppUpdate(true); }
5. 验证PackageName正确性
确保获取的packageName是Google Play上的正确包名,避免因包名错误导致API返回null。在_getPackageInfo中添加日志验证:
Future<void> _getPackageInfo() async { _logger.verbose('Getting package info...'); var packageInfo = await PackageInfo.fromPlatform(); installedVersion = packageInfo.version; packageName = packageInfo.packageName; appName = packageInfo.appName; _logger.info('Fetched packageName: $packageName'); // 添加日志验证 }
内容的提问来源于stack exchange,提问作者user19886724
相关产品推荐
相关产品推荐

