Skip to content

Commit 26d34e1

Browse files
committed
fix(android): don't crash in Fragment.onCreateView on stale-fragment race
onCreateView already returns null when its entry, resolvedPage, or frame is missing, but it first called Trace.error, which rethrows through the registered error handler across the JNI boundary and turns the recoverable case into a fatal "Calling js method onCreateView failed" crash. These nulls happen when the FragmentManager drives a stale fragment to CREATE_VIEW after NativeScript has already torn down the corresponding backstack entry (process death / activity recreation after rotation or theme change, clearHistory, or rapid navigate+goBack). The fragment should just be discarded, not crash the app. Downgrade the three guards to Trace.messageType.warn so the existing return-null recovery path is actually reached, consistent with the existing onDestroy handling of the same race.
1 parent 6bc5383 commit 26d34e1

1 file changed

Lines changed: 15 additions & 3 deletions

File tree

packages/core/ui/frame/frame-helper-for-android.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,33 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks
112112

113113
const entry = this.entry;
114114
if (!entry) {
115-
Trace.error(`${fragment}.onCreateView: entry is null or undefined`);
115+
// Recoverable race: a stale fragment is being driven to onCreateView by the
116+
// FragmentManager after its entry was cleared.
117+
// Using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary.
118+
if (Trace.isEnabled()) {
119+
Trace.write(`${fragment}.onCreateView: entry is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn);
120+
}
116121

117122
return null;
118123
}
119124

120125
const page = entry.resolvedPage;
121126
if (!page) {
122-
Trace.error(`${fragment}.onCreateView: entry has no resolvedPage`);
127+
// Logging via Trace.error (routes to an error handler, which throws) here is fatal because the registered error handler rethrows across the JNI boundary.
128+
if (Trace.isEnabled()) {
129+
Trace.write(`${fragment}.onCreateView: entry has no resolvedPage`, Trace.categories.NativeLifecycle, Trace.messageType.warn);
130+
}
123131

124132
return null;
125133
}
126134

127135
const frame = this.frame;
128136
if (!frame) {
129-
Trace.error(`${fragment}.onCreateView: this.frame is null or undefined`);
137+
// Recoverable race: the owning frame was already torn down. Returning null discards
138+
// this fragment; using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary.
139+
if (Trace.isEnabled()) {
140+
Trace.write(`${fragment}.onCreateView: this.frame is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn);
141+
}
130142

131143
return null;
132144
}

0 commit comments

Comments
 (0)