From f9541359b9b081752d58c3870e214fa787c1b10b Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 19 May 2026 23:34:44 -0300 Subject: [PATCH] fix: activity recreation crashes with tabs and complex view hierarchies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When "Don't keep activities" is enabled (or the system destroys the activity to reclaim memory), NativeScript apps using ViewPager2-based tabs (e.g. @nativescript-community/ui-material-tabs) crash on foregrounding. Two root causes are addressed: 1. ActionBar's disposeNativeView() nulled _actionItems, causing "Cannot read properties of null (reading 'addItem')" when the action bar was recreated during _setupUI after activity destruction. 2. Android's FragmentManager restores all fragments from savedInstanceState during super.onCreate(), including third-party fragments (ViewPager2's f0, f1, etc.). NativeScript's _tearDownUI/_setupUI cycle rebuilds the entire view tree from scratch, leaving these restored fragments orphaned — they reference container views that no longer exist, causing "Fragment does not have a view" errors. We now remove non-NativeScript fragments immediately after super.onCreate() while preserving core's own fragments (tagged fragment{id}[{depth}]) which have a proper restoration path via _processNextNavigationEntry and FragmentCallbacksImplementation.onCreateView. --- .../core/ui/action-bar/action-bar-common.ts | 1 - packages/core/ui/frame/index.android.ts | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/core/ui/action-bar/action-bar-common.ts b/packages/core/ui/action-bar/action-bar-common.ts index 6ffbd6f4a4..d2abc346d2 100644 --- a/packages/core/ui/action-bar/action-bar-common.ts +++ b/packages/core/ui/action-bar/action-bar-common.ts @@ -29,7 +29,6 @@ export class ActionBarBase extends View implements ActionBarDefinition { public effectiveContentInsetRight: number; disposeNativeView() { - this._actionItems = null; super.disposeNativeView(); } diff --git a/packages/core/ui/frame/index.android.ts b/packages/core/ui/frame/index.android.ts index b76a6fd80b..f0d8a57c47 100644 --- a/packages/core/ui/frame/index.android.ts +++ b/packages/core/ui/frame/index.android.ts @@ -861,6 +861,33 @@ export class ActivityCallbacksImplementation implements AndroidActivityCallbacks const isRestart = !!savedInstanceState && moduleLoaded; superFunc.call(activity, isRestart ? savedInstanceState : null); + if (isRestart && activity.getSupportFragmentManager) { + // Remove restored fragments that NativeScript will recreate via _setupUI/createNativeView. + // NativeScript tears down and rebuilds the entire view tree on activity recreation, + // so restored fragments (especially from ViewPager2/tabs) become orphaned without views. + // NativeScript core's own fragments use tags like "fragment{id}[{depth}]" and are + // handled by _processNextNavigationEntry. We remove all non-NativeScript fragments. + const fm = activity.getSupportFragmentManager(); + const fragments = fm.getFragments(); + if (fragments && fragments.size() > 0) { + const ft = fm.beginTransaction(); + let removed = false; + for (let i = fragments.size() - 1; i >= 0; i--) { + const f = fragments.get(i); + if (!f) continue; + const tag = f.getTag(); + if (tag && tag.startsWith('fragment')) { + continue; + } + ft.remove(f); + removed = true; + } + if (removed) { + ft.commitNowAllowingStateLoss(); + } + } + } + // Try to get the rootViewId form the saved state in case the activity // was destroyed and we are now recreating it. if (savedInstanceState) {