Skip to content

Commit c751404

Browse files
authored
Merge pull request #356 from nativescript-community/fix/fragment-destroying
fix: try to use the fragment's parent when possible
2 parents 8386631 + 7dc1998 commit c751404

File tree

3 files changed

+57
-18
lines changed

3 files changed

+57
-18
lines changed

src/bottom-navigation/index.android.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function initializeNativeClasses() {
113113

114114
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
115115
// TODO: Consider removing it when update to androidx.fragment:1.2.0
116-
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
116+
if (hasRemovingParent && this.owner.selectedIndex === this.index && this.owner.nativeViewProtected) {
117117
this.backgroundBitmap = this.loadBitmapFromView(this.owner.nativeViewProtected);
118118
}
119119

@@ -585,7 +585,11 @@ export class BottomNavigation extends TabNavigationBase {
585585
private hideFragment(fragment: androidx.fragment.app.Fragment, fragmentManager?: any) {
586586
if (!fragmentManager) {
587587
//@ts-ignore
588-
fragmentManager = this._getFragmentManager();
588+
fragmentManager = this._getParentFragmentManagerFromFragment(fragment);
589+
if(!fragmentManager) {
590+
// nothing to do
591+
return;
592+
}
589593
}
590594
if (fragment) {
591595
if (!fragment.isAdded() || fragment.isRemoving()) {
@@ -614,7 +618,11 @@ export class BottomNavigation extends TabNavigationBase {
614618
private showFragment(fragment: androidx.fragment.app.Fragment, fragmentManager?: any) {
615619
if (!fragmentManager) {
616620
//@ts-ignore
617-
fragmentManager = this._getFragmentManager();
621+
fragmentManager = this._getParentFragmentManagerFromFragment(fragment);
622+
if(!fragmentManager) {
623+
// nothing to do
624+
return;
625+
}
618626
}
619627
if (fragment) {
620628
if (!fragment.isAdded() || fragment.isRemoving()) {
@@ -643,7 +651,11 @@ export class BottomNavigation extends TabNavigationBase {
643651
private removeFragment(fragment: androidx.fragment.app.Fragment, fragmentManager?: any) {
644652
if (!fragmentManager) {
645653
//@ts-ignore
646-
fragmentManager = this._getFragmentManager();
654+
fragmentManager = this._getParentFragmentManagerFromFragment(fragment);
655+
if(!fragmentManager) {
656+
// nothing to do
657+
return;
658+
}
647659
}
648660
if (fragment) {
649661
if (!fragment.isAdded() || fragment.isRemoving()) {

src/core/tab-navigation-base/tab-navigation-base/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,21 @@ export class TabNavigationBase extends View implements TabNavigationBaseDefiniti
264264
// overridden by inheritors
265265
return null;
266266
}
267+
/**
268+
* Gets the parent fragment manager from a fragment to be used in destroying or hiding it.
269+
* @param fragment target fragment
270+
* @returns the parent fragment manager or null if none exists.
271+
*/
272+
public _getParentFragmentManagerFromFragment(fragment: androidx.fragment.app.Fragment) {
273+
if (!fragment) {
274+
return null;
275+
}
276+
try {
277+
return fragment.getParentFragmentManager();
278+
} catch (e) {
279+
return null;
280+
}
281+
}
267282
}
268283

269284
const MIN_ICON_SIZE = 24;

src/tabs/tabs.android.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function initializeNativeClasses() {
103103

104104
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
105105
// TODO: Consider removing it when update to androidx.fragment:1.2.0
106-
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
106+
if (hasRemovingParent && this.owner.selectedIndex === this.index && this.owner.nativeViewProtected) {
107107
this.backgroundBitmap = this.loadBitmapFromView(this.owner.nativeViewProtected);
108108
}
109109

@@ -200,18 +200,18 @@ function initializeNativeClasses() {
200200
}
201201

202202
destroyItem(container: android.view.ViewGroup, position: number, object: java.lang.Object): void {
203+
const fragment: androidx.fragment.app.Fragment = object as androidx.fragment.app.Fragment;
203204
if (!this.mCurTransaction) {
204-
const fragmentManager = this.owner._getFragmentManager();
205-
this.mCurTransaction = fragmentManager.beginTransaction();
205+
const fragmentManager: androidx.fragment.app.FragmentManager = this.owner._getParentFragmentManagerFromFragment(fragment);
206+
this.mCurTransaction = fragmentManager?.beginTransaction();
206207
}
207208

208-
const fragment: androidx.fragment.app.Fragment = object as androidx.fragment.app.Fragment;
209-
210-
const index = this.owner.fragments.indexOf(fragment);
209+
// detached fragments are still attached to the fragment manager
210+
// const index = this.owner.fragments.indexOf(fragment);
211211
// if (index !== -1) {
212212
// this.owner.fragments.splice(index, 1);
213213
// }
214-
this.mCurTransaction.detach(fragment);
214+
this.mCurTransaction?.detach(fragment);
215215

216216
if (this.mCurrentPrimaryItem === fragment) {
217217
this.mCurrentPrimaryItem = null;
@@ -479,7 +479,7 @@ export class Tabs extends TabsBase {
479479
return nativeView;
480480
}
481481
onSelectedIndexChanged(oldIndex: number, newIndex: number) {
482-
const tabBarImplementation = (this._tabsBar as unknown) as PositionChanger;
482+
const tabBarImplementation = this._tabsBar as unknown as PositionChanger;
483483
if (tabBarImplementation) {
484484
tabBarImplementation.onSelectedPositionChange(oldIndex, newIndex);
485485
}
@@ -621,14 +621,26 @@ export class Tabs extends TabsBase {
621621
}
622622

623623
private disposeCurrentFragments(): void {
624-
const fragmentManager = this._getFragmentManager();
625-
const transaction = fragmentManager.beginTransaction();
624+
// we need to use this because the destroyItem only detaches the item
625+
// here we clean up all fragments, even ones that were detached to another manager, which may happen on suspend/resume
626+
// alternative: actually remove the fragment on destroyItem
627+
const transactionMap = new Map<androidx.fragment.app.FragmentManager, androidx.fragment.app.FragmentTransaction>();
628+
for (const fragment of this.fragments) {
629+
const fragmentManager = this._getParentFragmentManagerFromFragment(fragment);
630+
if (!fragmentManager || fragmentManager.isDestroyed()) {
631+
continue;
632+
}
633+
if (!transactionMap.has(fragmentManager)) {
634+
transactionMap.set(fragmentManager, fragmentManager.beginTransaction());
635+
}
636+
const transaction = transactionMap.get(fragmentManager);
626637

627-
const fragments = this.fragments;
628-
for (let i = 0; i < fragments.length; i++) {
629-
transaction.remove(fragments[i]);
638+
transaction.remove(fragment);
639+
}
640+
for (const transaction of transactionMap.values()) {
641+
transaction.commitNowAllowingStateLoss();
630642
}
631-
transaction.commitNowAllowingStateLoss();
643+
transactionMap.clear(); // let's avoid memory leaks
632644
this.fragments = [];
633645
}
634646

0 commit comments

Comments
 (0)