Improve app cold start performance
這是什麼類型的 PR?
選擇所有符合的項目
- [ ] 重構
- [ ] 新功能
- [ ] 錯誤修復
- [x] 最佳化
- [ ] 技術文件更新
描述
改善 冷啟動時間 初次啟動完,下次改背景跑,以節省冷啟動時間 (fcmInit, 跟 notifyInit)
部分Android 裝置 原本4-6 秒以上啟動完成 節省約1-2 秒 (裝置效能最差不一定) 也可能改善熱啟動緩慢問題
iOS 毫無感覺 通常1-1.5 秒內啟動完成
相關 issue
- 相關問題 #
- closes #
QA 指南、截圖、錄像
請將這行替換成:如何測試您的 PR 的步驟,已測試的裝置註釋,以及任何相關的 UI 更改圖片。
UI 無障礙清單
如果您的 PR 包含 UI 更改,請使用此清單:
- [ ] 變數名稱實現語意化命名?
- [ ] 測試通過 AA 顏色對比?
Claude finished @lowrt's task —— View job
Code Review Complete ✅
Summary
I've reviewed this performance optimization PR that aims to improve cold start time by deferring FCM and notification initialization to run in the background after the first launch. The approach is sound and aligns with Flutter performance best practices, but there are several important issues and improvements to address.
🔴 Critical Issues
1. Typo in Log Message (lib/main.dart:67)
talker.log('🟣 首次啟動 → 前置初始化 FCN + 通知');
Issue: "FCN" should be "FCM" (Firebase Cloud Messaging).
Fix: Change to '🟣 首次啟動 → 前置初始化 FCM + 通知'
2. Missing Error Handling for Background Tasks (lib/main.dart:112-114)
unawaited(Future(() => fcmInit()));
unawaited(Future(() => notifyInit()));
unawaited(Future(() => updateInfoToServer()));
Issue: These background tasks have no error handling. If fcmInit() or notifyInit() fail, users won't receive notifications and there will be no error reporting. This is a critical functional issue.
Impact: Silent failures could leave the app in a broken state where:
- Push notifications don't work
- Local notifications fail
- No crash reports or logs are generated
Recommended Fix:
if (!isFirstLaunch) {
talker.log('🟢 非首次啟動 → 通知與 FCM 改為背景初始化');
unawaited(Future(() async {
try {
await fcmInit();
} catch (e, stack) {
talker.error('背景 FCM 初始化失敗', e, stack);
}
}));
unawaited(Future(() async {
try {
await notifyInit();
} catch (e, stack) {
talker.error('背景通知初始化失敗', e, stack);
}
}));
unawaited(Future(() async {
try {
await updateInfoToServer();
} catch (e, stack) {
talker.error('背景伺服器更新失敗', e, stack);
}
}));
}
🟡 Important Considerations
3. Race Condition Risk
Issue: After runApp() is called, background initialization tasks may not complete before the user interacts with notification-dependent features.
Scenario:
- User launches app (not first time)
- App UI renders immediately
- User tries to interact with a feature requiring notifications
-
notifyInit()hasn't completed yet → potential crash or undefined behavior
Questions to consider:
- Does the app have guards/checks before using notification features?
- Are there UI elements that depend on FCM/notifications being ready?
- Should there be a loading state for notification-dependent features?
Recommendation: Add defensive checks in code that depends on these services:
if (notificationInitialized) {
// Use notification features
} else {
// Show loading or disable feature
}
4. First Launch Detection Logic (lib/main.dart:50)
final isFirstLaunch = Preference.instance.getBool('isFirstLaunch') ?? true;
Issue: The default value is true, which means:
- If the preference fails to load, it's treated as first launch
- This could cause unnecessary blocking initialization on subsequent launches
Impact: Not critical, but could affect performance measurement accuracy.
Recommendation: Consider logging when the preference is null to help debug edge cases.
5. Performance Testing Evidence
Issue: The PR description mentions "安卓 節省約1~2秒" but doesn't include:
- Before/after metrics
- Testing methodology
- Device specifications
- Sample size
Recommendation: Add performance metrics to the PR description:
- Startup time before optimization
- Startup time after optimization
- Testing devices and Android versions
- Number of test runs
✅ What's Done Well
- Smart Optimization Strategy: Deferring non-critical initialization is a proven performance optimization technique
- Comprehensive Logging: Excellent use of timing logs to measure performance
- First Launch Handling: Smart decision to initialize FCM/notifications synchronously on first launch to ensure proper setup
- Code Cleanliness: Good refactoring of log messages for consistency
-
Use of
unawaited: Correct use ofdart:async'sunawaitedto explicitly mark fire-and-forget futures
🔍 Code Quality & Best Practices
Minor Issues:
-
Unused Variable Cleanup (lib/main.dart:46-47, 63-64, etc.) The refactoring to inline duration calculations is good and removes unnecessary variables. ✅
-
Comment Removal (lib/main.dart:138) Removing the comment
// 確保失敗時應用程式會報錯is fine sincerethrowis self-explanatory. ✅
🧪 Testing Recommendations
Since this is a performance optimization affecting app initialization:
- Test on cold start: Force stop app and relaunch
- Test on various Android devices: Low-end, mid-range, high-end
- Test notification functionality: Verify notifications work after background init
- Test FCM: Verify push notifications arrive correctly
- Test error scenarios: What happens if FCM/notify init fails?
- Measure actual impact: Use Android Studio Profiler or similar tools
Missing: The PR checklist shows no QA guide or testing steps. Please add:
- How to test the changes
- Expected behavior
- Devices tested
📊 Performance Analysis
The Good:
- Moving
fcmInit(),notifyInit(), andupdateInfoToServer()out of the critical path is excellent - This should indeed improve perceived startup time by 1-2 seconds
The Concern:
- Background initialization might not complete before user needs those features
- No performance metrics in the PR to validate the claimed improvement
🎯 Recommendations Summary
Must Fix:
- ✅ Fix typo: FCN → FCM
- ⚠️ Add error handling to background initialization tasks
Should Consider: 3. Add defensive checks for notification/FCM availability 4. Add performance metrics to PR description 5. Document testing methodology 6. Update QA guide section in PR description
Nice to Have: 7. Add state management for initialization completion 8. Consider showing loading indicator for notification features
Final Verdict
Overall: This is a solid performance optimization with the right approach. However, the lack of error handling for background tasks is a critical issue that must be addressed before merging. The performance gains are worthwhile, but we need to ensure the app remains stable and functional.
Recommendation: Request changes to add error handling, then approve after fixes.
Claude finished @lowrt's task —— View job
Code Review Complete ✅
I've reviewed the performance optimization changes. This PR implements a smart cold start optimization by deferring FCM and notification initialization to run in the background after the first launch. Overall, the approach is sound and follows Flutter best practices, with good improvements made based on the previous review.
Summary of Changes
The PR modifies the app initialization flow to:
-
First launch: Initialize FCM and notifications synchronously (before
runApp()) -
Subsequent launches: Defer FCM and notification initialization to background (after
runApp()) - Removes these tasks from the main
Future.wait()parallel task group - Adds error handling for background initialization
Expected benefit: 1-2 seconds faster startup on Android devices (for non-first launches)
✅ What's Done Well
-
Smart optimization strategy: Moving non-critical initialization post-
runApp()is a proven technique for improving perceived startup time - First launch safety: Correctly handles first launch synchronously to ensure proper setup
- Error handling added: Good error handling for background tasks with proper logging (addresses previous review feedback)
- Comprehensive logging: Excellent performance tracking with detailed timing logs
- Code cleanup: Inlined duration calculations, removed unnecessary variables, cleaner log messages
-
Proper use of
unawaited: Correctly marks fire-and-forget futures
🟡 Potential Issues & Considerations
1. Race Condition Risk (Medium Priority)
Issue: After runApp() executes (line 80-109), the UI is immediately available but fcmInit() and notifyInit() may not have completed yet.
Scenario:
1. User launches app (2nd+ time)
2. runApp() renders UI immediately
3. User interacts with notification features ← potential issue
4. Background init still running → undefined behavior
Questions to verify:
- Does the app have guards checking if notifications are ready before use?
- Are there UI elements that depend on FCM/notifications being initialized?
- What happens if a push notification arrives before
fcmInit()completes?
Recommendation: Consider adding state tracking:
// In appropriate provider/state management
bool notificationReady = false;
bool fcmReady = false;
Then guard notification-dependent features:
if (GlobalProviders.notification.isReady) {
// Use notification features
} else {
// Show loading or gracefully handle
}
2. Silent Failure Scenario (Low Priority)
While error handling exists (line 117-119), if background initialization fails:
- Users won't receive notifications until app restart
- No user-visible indication of the problem
- Only logged to console/crash reporting
Recommendation: Consider adding retry logic or user notification for critical failures.
3. Performance Metrics Missing
The PR description mentions "節省約1-2秒" but doesn't include:
- Before/after measurements
- Test devices and Android versions
- Testing methodology
- Number of test runs
Recommendation: Add performance data to validate the optimization:
Testing results:
- Device: [model name]
- Android version: X
- Before: Xms average (N runs)
- After: Yms average (N runs)
- Improvement: Zms (Z%)
4. updateInfoToServer() Handling
First launch (line 72): updateInfoToServer() is fire-and-forget with unawaited
Subsequent launches (line 116): updateInfoToServer() is awaited inside the background task
Question: Is this intentional? Should the behavior be consistent?
🔍 Code Quality Notes
Minor observations:
-
Line 67: Typo fixed - Changed from "FCN" to "FCM" ✅ (if this was in previous version)
-
Line 110-121: The error handling correctly catches and logs exceptions ✅
-
Line 112-120: All three initialization tasks share the same error handler - if one fails, the others may not run. Consider individual try-catch blocks if partial initialization is acceptable:
unawaited(Future(() async { try { await fcmInit(); } catch (e, st) { talker.error('FCM失敗: $e\n$st'); } try { await notifyInit(); } catch (e, st) { talker.error('通知失敗: $e\n$st'); } try { await updateInfoToServer(); } catch (e, st) { talker.error('伺服器更新失敗: $e\n$st'); } })); -
Import addition:
dart:asyncproperly added forunawaited✅
🧪 Testing Recommendations
To validate this optimization:
-
Cold start testing:
- Force stop app
- Clear from recents
- Measure time to interactive
-
Notification functionality:
- Test local notifications work after background init
- Test push notifications arrive correctly
- Test notification actions/interactions
-
Error scenarios:
- Test with airplane mode (FCM fails)
- Test with notification permissions denied
- Verify error logging works
-
Device variety:
- Low-end Android devices (critical for performance)
- Mid-range devices
- Various Android versions
-
Edge cases:
- App killed during background init
- Multiple rapid launches
- Background init while app is backgrounded
📊 Performance Analysis
The optimization is sound:
- Moving
fcmInit(),notifyInit(), andupdateInfoToServer()out of the critical path should improve startup - First launch is protected with synchronous initialization
- Background tasks won't block UI rendering
Potential concern:
- The actual speedup depends on how long FCM/notification init takes
- If these are already fast (<100ms), the perceived improvement may be minimal
- If they're slow (1-2s as claimed), this optimization is highly valuable
🎯 Recommendations Summary
Before merging:
- ✅ Error handling is present - looks good
- ⚠️ Verify no race conditions exist in notification-dependent features
- 📊 Add performance measurements to PR description
- 🧪 Test notification functionality after background init
Nice to have:
- Add retry logic for failed background init
- Consider state tracking for initialization completion
- Add user-facing indication if critical services fail to initialize
- Make
updateInfoToServer()behavior consistent between first/subsequent launches
Final Verdict
Overall: This is a well-implemented performance optimization that follows best practices. The code quality is good, error handling is present, and the approach is sound. The main concern is ensuring no race conditions exist in features that depend on notifications/FCM.
Status: ✅ Approved with suggestions - The changes are solid and ready to merge, but please:
- Verify notification features have proper guards
- Add performance metrics to the PR description
- Test thoroughly on target Android devices
The optimization should provide meaningful startup improvements on Android devices, especially lower-end ones where every second counts.
Claude finished @lowrt's task —— View job
Code Review Complete ✅
I've reviewed the performance optimization changes in this PR. The implementation shows good improvements over the previous version, with proper error handling added. Here's my detailed analysis:
Summary of Changes
This PR optimizes app cold start performance by:
-
First launch: Initialize FCM and notifications synchronously (blocking, before
runApp()) -
Subsequent launches: Defer FCM and notification initialization to background (non-blocking, after
runApp()) - Removes
fcmInit(),notifyInit(), andupdateInfoToServer()from the mainFuture.wait()parallel task group - Adds iOS-specific guard for notification permission checks (lib/app.dart:55)
- Defers
_checkNotificationPermission()with 500ms delay (lib/app.dart:97)
Expected benefit: 1-2 seconds faster startup on Android devices (non-first launches)
✅ Improvements Since Last Review
- Error handling added (lib/main.dart:113-119): Background initialization tasks now have proper try-catch with error logging
- Platform-specific logic (lib/app.dart:55): Added iOS-only check for notification permissions
-
Deferred permission check (lib/app.dart:94-99):
_checkNotificationPermission()now runs after 500ms delay in postFrameCallback - Clean code: Inline duration calculations, removed unnecessary variables, cleaner log messages
🔴 Critical Issues
1. Race Condition: Notification Permission Check Before FCM Init (lib/app.dart:97)
Location: lib/app.dart:94-99
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(const Duration(milliseconds: 500), () {
_handlePendingNotificationWhenReady();
_checkNotificationPermission(); // ← Calls FirebaseMessaging.instance
});
});
Issue: On iOS non-first launches, _checkNotificationPermission() is called 500ms after app start, but fcmInit() runs in background (lib/main.dart:114) with no guaranteed completion time.
Problem:
-
FirebaseMessaging.instance.getNotificationSettings()(lib/app.dart:57) may be called before Firebase is fully initialized - This could cause a crash or undefined behavior on iOS
Why this matters: The 500ms delay assumes FCM will be ready, but on slower devices or under heavy load, fcmInit() in the background might take longer than 500ms.
Recommendation: Add a guard or ensure FCM is initialized before checking settings:
Future<void> _checkNotificationPermission() async {
if (Platform.isIOS) {
// Wait for FCM to be ready or check if initialized
bool notificationAllowed = false;
try {
final settings = await FirebaseMessaging.instance.getNotificationSettings();
notificationAllowed = settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
// ... rest of logic
} catch (e) {
// FCM not ready yet, skip for now
return;
}
}
}
Severity: High on iOS, particularly on slower devices
🟡 Important Considerations
2. Race Condition: Background Init vs. Feature Usage (lib/main.dart:110-121)
Issue: After runApp() executes, UI is immediately interactive but background initialization may not have completed.
Scenario:
- User launches app (2nd+ time)
-
runApp()renders UI immediately at line 80 - User tries to interact with notification features ← 100-200ms after launch
- Background init (line 112-120) may still be running → potential undefined behavior
Questions to verify:
- Does the app check if FCM/notifications are ready before using them?
- What happens if a push notification arrives before
fcmInit()completes? - Are there UI elements that depend on notification readiness?
Recommendation: Add defensive checks in code that depends on these services, or add state tracking for initialization completion.
3. Inconsistent updateInfoToServer() Handling
First launch (lib/main.dart:72):
unawaited(Future(() => updateInfoToServer()));
Fire-and-forget, not awaited
Subsequent launches (lib/main.dart:116):
await updateInfoToServer();
Awaited inside the background task, sequential after notifyInit()
Question: Is this intentional?
- Should it always be fire-and-forget for consistency?
- Or should first launch also await it within the initialization block?
Impact: On first launch, updateInfoToServer() runs truly in parallel. On subsequent launches, it waits for fcmInit() and notifyInit() to complete first.
4. Error Handling Cascade (lib/main.dart:113-119)
try {
await fcmInit();
await notifyInit();
await updateInfoToServer();
} catch (e, st) {
talker.error('背景初始化失敗: $e\n$st');
}
Issue: If fcmInit() fails, notifyInit() and updateInfoToServer() won't run at all.
Consideration: Should partial initialization be allowed? If so, use individual try-catch:
unawaited(Future(() async {
try {
await fcmInit();
} catch (e, st) {
talker.error('背景 FCM 初始化失敗: $e\n$st');
}
try {
await notifyInit();
} catch (e, st) {
talker.error('背景通知初始化失敗: $e\n$st');
}
try {
await updateInfoToServer();
} catch (e, st) {
talker.error('背景伺服器更新失敗: $e\n$st');
}
}));
Trade-off: Current approach is simpler but more fragile. Individual try-catch is more resilient but more verbose.
🟢 What's Done Well
-
Smart optimization strategy: Moving non-critical init post-
runApp()is proven best practice ✅ - First launch safety: Synchronous init on first launch ensures proper setup ✅
- Error handling present: Background tasks have try-catch with logging ✅
- Comprehensive logging: Excellent performance tracking with timing logs ✅
- Code cleanup: Inlined calculations, removed unnecessary variables ✅
-
Proper use of
unawaited: Correctly marks fire-and-forget futures ✅ - Platform-specific logic: iOS-only notification check is appropriate ✅
-
Import added:
dart:asyncproperly imported forunawaited✅
🧪 Testing Recommendations
Critical tests (focus on these):
-
iOS notification check timing:
- Launch app on iOS (2nd+ time)
- Verify no crash when checking notification settings
- Test on slower iOS devices (iPhone 8, SE)
-
Background init completion:
- Launch app (2nd+ time)
- Immediately trigger notification-dependent features (< 500ms)
- Verify app doesn't crash or behave unexpectedly
-
FCM functionality:
- Verify push notifications work after background init
- Test receiving notification immediately after app launch
-
Error scenarios:
- Test with airplane mode (FCM/updateInfoToServer fails)
- Test with notification permissions denied
- Verify error logging works and app remains stable
-
Performance validation:
- Measure actual cold start time before/after
- Test on low-end Android devices (critical)
- Test on various Android versions (8-14)
📊 Performance Analysis
The optimization is sound:
- Removing 3 tasks from critical path should improve startup ✅
- First launch protection ensures proper setup ✅
- Background tasks won't block UI rendering ✅
Expected improvement:
- Android: 1-2 seconds faster (as claimed) - likely achievable if
fcmInit()/notifyInit()are slow - iOS: Minimal impact (already fast at 1-1.5s)
To validate: Add actual before/after metrics to PR description with device specs.
🎯 Recommendations Summary
Must address before merging:
- 🔴 Fix iOS race condition: Ensure
_checkNotificationPermission()doesn't crash when FCM isn't ready (add try-catch or wait for FCM)
Should consider:
2. ⚠️ Add defensive checks for notification/FCM availability in features that use them
3. 🤔 Make updateInfoToServer() behavior consistent between first/subsequent launches
4. 🛡️ Consider individual try-catch for partial initialization resilience
5. 📊 Add performance metrics (before/after) to PR description
Nice to have: 6. Add state tracking for initialization completion 7. Add retry logic for failed background init 8. Document testing methodology in PR QA section
Final Verdict
Overall Assessment: This is a well-implemented performance optimization that follows Flutter best practices. The code quality is good and error handling has been added as requested. However, there's a potential race condition on iOS (Issue #1) that should be addressed before merging.
Status: ⚠️ Approve with Required Changes
Required changes:
- Add error handling to
_checkNotificationPermission()to prevent crashes if FCM isn't initialized yet
After fixing: This PR will provide meaningful startup improvements on Android devices while maintaining stability across platforms.
@claude review
Claude finished @lowrt's task —— View job
Code Review Complete ✅
I've thoroughly reviewed this performance optimization PR. The latest changes show significant improvements based on previous feedback, particularly the addition of a Completer to safely synchronize FCM initialization on iOS. Here's my detailed analysis:
Summary of Changes
This PR optimizes cold start performance by:
-
First launch: Initialize FCM and notifications synchronously (blocking, before
runApp()) -
Subsequent launches: Defer FCM, notifications, and server updates to background (non-blocking, after
runApp()) - Adds
fcmReadyCompleterto ensure FCM is initialized before checking notification permissions on iOS - Platform-specific guard: iOS-only notification permission check (lib/app.dart:56)
- Removes
fcmInit(),notifyInit(), andupdateInfoToServer()from main parallel task group
Expected benefit: 1-2 seconds faster startup on Android (non-first launches)
✅ Major Improvements Since Last Review
-
fcmReadyCompleteradded (lib/main.dart:24): Excellent solution to prevent race conditions on iOS -
iOS-specific wait (lib/app.dart:57):
_checkNotificationPermission()now waits for FCM to be ready before checking settings - Completer completion (lib/core/fcm.dart:26-28): FCM properly signals when initialization is complete on iOS
- Error handling (lib/main.dart:118-119): Background tasks have try-catch with proper error logging
- Code cleanup: Inline duration calculations, cleaner log messages, removed unnecessary variables
This addresses the critical race condition identified in previous reviews where FirebaseMessaging.instance.getNotificationSettings() could be called before Firebase was initialized.
🟡 Remaining Issues & Considerations
1. Completer Not Completed on Android (Medium Priority)
Location: lib/core/fcm.dart:10-29
Issue: The fcmReadyCompleter is only completed on iOS (line 26-28), but not on Android.
Future<void> fcmInit() async {
await Firebase.initializeApp();
if (Platform.isAndroid) {
await AwesomeNotificationsFcm().initialize(/* ... */);
await AwesomeNotificationsFcm().requestFirebaseAppToken();
// ⚠️ Missing: fcmReadyCompleter.complete()
} else if (Platform.isIOS) {
Preference.notifyToken = await FirebaseMessaging.instance.getAPNSToken();
if (!fcmReadyCompleter.isCompleted) {
fcmReadyCompleter.complete(); // ✅ Only completed here
}
}
}
Impact:
- On iOS: The early return at lib/app.dart:56 prevents the issue
- However, if future code needs to wait for FCM on Android, it will hang forever
- For code consistency and safety, the completer should be completed on both platforms
Recommendation: Add completer completion for Android:
if (Platform.isAndroid) {
await AwesomeNotificationsFcm().initialize(/* ... */);
await AwesomeNotificationsFcm().requestFirebaseAppToken();
if (!fcmReadyCompleter.isCompleted) {
fcmReadyCompleter.complete();
}
} else if (Platform.isIOS) {
// existing code
}
2. Race Condition: Background Init vs Feature Usage (Low-Medium Priority)
Location: lib/main.dart:111-121
Issue: After runApp() (line 81), the UI is immediately interactive but background initialization may not have completed.
Scenario:
1. User launches app (2nd+ time)
2. runApp() renders UI at line 81
3. User interacts with notification features at ~100ms
4. Background init (line 112-120) still running → potential issues
Questions to verify:
- Does the app check if FCM/notifications are ready before using them in features?
- What happens if a push notification arrives before
fcmInit()completes? - Are there guards in notification-dependent UI components?
Current protection: iOS has fcmReadyCompleter wait in _checkNotificationPermission(), which is good. But other potential notification features may not have this protection.
Recommendation:
- Document that
fcmReadyCompleter.futureshould be awaited before using Firebase Messaging features - Add defensive checks in notification-dependent code if needed
3. Error Handling Cascade (Low Priority)
Location: lib/main.dart:114-119
try {
await fcmInit();
await notifyInit();
await updateInfoToServer();
} catch (e, st) {
talker.error('背景初始化失敗: $e\n$st');
}
Issue: If fcmInit() fails, notifyInit() and updateInfoToServer() won't run.
Trade-off consideration:
- Current approach: Simple, fails fast if FCM fails (reasonable since notifications depend on FCM)
- Alternative: Individual try-catch blocks allow partial initialization
Verdict: Current approach is acceptable because:
-
notifyInit()likely depends onfcmInit()succeeding -
updateInfoToServer()is not critical - Clean error logging is present
Optional improvement (if updateInfoToServer() should always run):
try {
await fcmInit();
await notifyInit();
} catch (e, st) {
talker.error('背景 FCM/通知初始化失敗: $e\n$st');
}
// Always try to update server
try {
await updateInfoToServer();
} catch (e, st) {
talker.error('背景伺服器更新失敗: $e\n$st');
}
4. Inconsistent updateInfoToServer() Handling (Low Priority)
First launch (lib/main.dart:73):
unawaited(Future(() => updateInfoToServer()));
Fire-and-forget, not awaited
Subsequent launches (lib/main.dart:117):
await updateInfoToServer();
Awaited sequentially after fcmInit() and notifyInit()
Question: Is this intentional?
Analysis:
- First launch: Runs truly in parallel with
Future.wait() - Subsequent launches: Waits for FCM and notify to complete first
Impact: Minor inconsistency, but probably acceptable if server update can happen independently.
Recommendation: For consistency, consider making both fire-and-forget or add a comment explaining the difference.
🟢 What's Done Well
- ✅ Smart optimization: Moving non-critical init post-
runApp()follows Flutter best practices - ✅ Race condition fix:
fcmReadyCompleterelegantly solves iOS notification permission check timing - ✅ First launch safety: Synchronous init ensures proper setup on first launch
- ✅ Error handling: Background tasks have proper try-catch with logging
- ✅ Platform-specific logic: Android early return (line 56) is clean and appropriate
- ✅ Comprehensive logging: Excellent performance tracking with detailed timing logs
- ✅ Code cleanup: Inlined calculations, removed unnecessary variables, cleaner messages
- ✅ Proper use of
unawaited: Correctly marks fire-and-forget futures - ✅ Import management:
dart:asyncproperly added forunawaitedandCompleter
🧪 Testing Recommendations
Critical tests:
-
iOS notification permission check:
- Launch app on iOS (2nd+ time)
- Verify no crash when checking notification settings
- Confirm
fcmReadyCompleterprevents race condition
-
Background initialization completion:
- Launch app (2nd+ time)
- Monitor logs to verify background init completes successfully
- Test on slower Android devices
-
Notification functionality:
- Verify push notifications work after background init
- Test receiving notification immediately after app launch (< 1 second)
- Test local notifications
-
Error scenarios:
- Test with airplane mode (FCM/updateInfoToServer should fail gracefully)
- Test with notification permissions denied
- Verify error logging works and app remains stable
-
Performance validation:
- Measure actual cold start time before/after on low-end Android devices
- Validate the claimed 1-2 second improvement
- Test on various Android versions (8-14)
📊 Performance Analysis
The optimization approach is sound:
- ✅ Removing 3 async tasks from critical path should improve startup
- ✅ First launch protection ensures proper setup
- ✅ Background tasks won't block UI rendering
- ✅
fcmReadyCompleterprevents iOS race condition without blocking startup
Expected improvements:
-
Android: 1-2 seconds faster (as claimed) - achievable if
fcmInit()/notifyInit()are slow - iOS: Minimal impact (already fast at 1-1.5s), but now safer with completer
To validate: Add before/after metrics with device specs to PR description.
🎯 Recommendations Summary
Should fix (not blocking, but recommended):
- ⚠️ Complete
fcmReadyCompleteron Android too (for code consistency and safety)
Nice to have:
2. Document when/where to use await fcmReadyCompleter.future in code that uses Firebase Messaging
3. Consider making updateInfoToServer() handling consistent between first/subsequent launches
4. Add performance metrics (before/after) to PR description with device specs
5. Fill out the QA guide section in PR description
Already addressed from previous reviews:
- ✅ Error handling added
- ✅ iOS race condition solved with completer
- ✅ Platform-specific guards in place
Final Verdict
Overall Assessment: This is a well-implemented performance optimization that addresses the critical race condition from previous reviews. The addition of fcmReadyCompleter is an elegant solution that prevents crashes on iOS while maintaining the performance benefits. The code quality is good, error handling is solid, and the approach follows Flutter best practices.
Status: ✅ Approved
The one minor issue (completing the completer on Android) is not blocking because:
- The current code works correctly on both platforms
- iOS early-returns before waiting, so Android path doesn't cause issues
- It's more of a code consistency/future-proofing concern
This PR is ready to merge. The optimization should provide meaningful startup improvements on Android devices while maintaining stability across both platforms.
Great work addressing the previous feedback! The fcmReadyCompleter solution shows thoughtful engineering.