fix: [SDK-4722] guard getNotifications() in onDetachedFromEngine (#1149)#1150
Conversation
There was a problem hiding this comment.
The native-side fix looks correct and well-scoped, but this PR also deletes .github/pull_request_template.md entirely — which isn't mentioned in the description and looks unintentional (likely the template content was pulled into the PR body and the file ended up removed). Could you confirm whether that deletion is intentional before merge?
Extended reasoning...
Overview
The functional change is a single guard added at the top of OneSignalNotifications.onDetachedFromEngine() (android/src/main/java/com/onesignal/flutter/OneSignalNotifications.java:231-234): an early return when OneSignal.isInitialized() is false, with a comment pointing to issue #1149. This is a small, targeted Android-only fix for an IllegalStateException thrown by the native SDK when the Flutter engine detaches before OneSignal.initialize() ran. Behavior when initialized is unchanged.
Security risks
None. The change adds a defensive guard around an SDK call and reduces a crash surface; it does not touch auth, crypto, permissions, networking, or data handling.
Level of scrutiny
The Java change itself is low-risk and approve-worthy on its own — it follows the existing pattern (OneSignal.isInitialized() is the documented native check) and the reasoning is correct: if the SDK was never initialized, there cannot be a click listener registered for it to remove, so the early return is safe. The recent commit 05e3d8d fix(android): guard getNotifications() before init suggests the same pattern was already applied elsewhere, so this is consistent.
Other factors
What pushed this to a deferral is the second file change: the PR removes .github/pull_request_template.md entirely. The PR description does not mention this, the title/scope are explicitly about the Android guard, and the deletion looks like an editor/Cursor artifact (the template content appears verbatim inside the PR body, suggesting the author drafted on top of the file and the original got deleted). A human should confirm whether the template removal is intentional — if not, it should be restored in a follow-up commit before merge. No bugs were reported by the bug-hunting pass.
Description
One Line Summary
Guard
OneSignal.getNotifications()inonDetachedFromEngineso the Flutter plugin no longer crashes on Android when the engine detaches beforeOneSignal.initialize()ran.Details
Motivation
Fixes #1149. On Android, when the app is closed (
Activity#onDestroy→FlutterPlugin#onDetachedFromEngine) withoutOneSignal.initialize()having been called from Dart, the plugin'sOneSignalNotifications.onDetachedFromEngine()callsOneSignal.getNotifications().removeClickListener(this). The native SDK throws:This happens for users who include the plugin via
pubspec.yamlbut defer or skipOneSignal.initialize()(cold start, conditional init, fast finish, etc.). The plugin still auto-attaches to the engine, so detach still fires.Scope
OneSignal.isInitialized()guard at the top ofOneSignalNotifications.onDetachedFromEngine().Testing
Manual testing
Reproduced and verified on a Pixel emulator (Android 16) using a stripped-down repro app on branch
repro/issue-1149-android-on-detach-crash(noOneSignal.initialize()call, a button that callsActivity#finish()via a nativeMethodChannel).Steps:
adb uninstall com.onesignal.example(so the nativeSyncJobServicefrom a prior install does not auto-callinitWithContextin the background and mask the bug).flutter runfromexamples/demo.adb logcat.Results:
repro/issue-1149-android-on-detach-crash):FATAL EXCEPTION ... IllegalStateException: Must call 'initWithContext' before useatOneSignalNotifications.onDetachedFromEngine.repro/issue-1149-fix, same repro app + this patch):MainActivity.onDestroylogs, no exception, process exits cleanly.Also re-ran the normal demo flow with
OneSignal.initialize()enabled to confirm the click listener is still removed on detach and no regression in normal init paths.Affected code checklist
Checklist
Overview
Testing
FlutterPlugin#onDetachedFromEnginelifecycle plus the native OneSignal SDK's uninitialized state, which isn't exercisable from the existing Dart test surface. Verified via the repro app described above.Final pass
Made with Cursor