Skip to content

Commit

Permalink
[android] Fix re-attaching ScreenContainer to window (#272)
Browse files Browse the repository at this point in the history
Code to reproduce and test: https://snack.expo.io/@angelikaserwa/humiliated-bagel.
Switch to the Settings tab, then go back to the Home tab and press the details button. Nothing happens. It was because after re-attaching we were using a destroyed FragmentManager that was cached inside the ScreenContainer class. 
Then, when we go back from Details to Home screen, using a hardware back button, an exception occured: `The specified child already has a parent. You must call removeView() on the child's parent first`.
I fixed this by calling `removeAllViews()` when detaching container from window and forcing an update on re-attach.
  • Loading branch information
graszka22 authored and kmagiera committed Jan 17, 2020
1 parent 518c094 commit 3fc74e2
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 22 deletions.
19 changes: 10 additions & 9 deletions android/src/main/java/com/swmansion/rnscreens/ScreenContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
private final Set<ScreenFragment> mActiveScreenFragments = new HashSet<>();
private final ArrayList<Runnable> mAfterTransitionRunnables = new ArrayList<>(1);

private @Nullable FragmentManager mFragmentManager;
protected @Nullable FragmentManager mFragmentManager;
private @Nullable FragmentTransaction mCurrentTransaction;
private @Nullable FragmentTransaction mProcessingTransaction;
private boolean mNeedUpdate;
Expand Down Expand Up @@ -184,16 +184,9 @@ private FragmentManager findFragmentManager() {
return ((FragmentActivity) context).getSupportFragmentManager();
}

protected final FragmentManager getFragmentManager() {
if (mFragmentManager == null) {
mFragmentManager = findFragmentManager();
}
return mFragmentManager;
}

protected FragmentTransaction getOrCreateTransaction() {
if (mCurrentTransaction == null) {
mCurrentTransaction = getFragmentManager().beginTransaction();
mCurrentTransaction = mFragmentManager.beginTransaction();
mCurrentTransaction.setReorderingAllowed(true);
}
return mCurrentTransaction;
Expand Down Expand Up @@ -247,13 +240,21 @@ protected boolean hasScreen(ScreenFragment screenFragment) {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mIsAttached = true;
mFragmentManager = findFragmentManager();
updateIfNeeded();
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsAttached = false;

// fragment manager is destroyed so we can't do anything with it anymore
mFragmentManager = null;
// so we don't add the same screen twice after re-attach
removeAllViews();
// after re-attach we'll update the screen and add views again
markUpdated();
}

@Override
Expand Down
24 changes: 11 additions & 13 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
private final FragmentManager.OnBackStackChangedListener mBackStackListener = new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
if (mFragmentManager.getBackStackEntryCount() == 0) {
// when back stack entry count hits 0 it means the user's navigated back using hw back
// button. As the "fake" transaction we installed on the back stack does nothing we need
// to handle back navigation on our own.
Expand Down Expand Up @@ -70,24 +70,22 @@ protected ScreenStackFragment adapt(Screen screen) {

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
FragmentManager fm = getFragmentManager();
fm.removeOnBackStackChangedListener(mBackStackListener);
getFragmentManager().unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks);
if (!fm.isStateSaved()) {
mFragmentManager.removeOnBackStackChangedListener(mBackStackListener);
mFragmentManager.unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks);
if (!mFragmentManager.isStateSaved()) {
// state save means that the container where fragment manager was installed has been unmounted.
// This could happen as a result of dismissing nested stack. In such a case we don't need to
// reset back stack as it'd result in a crash caused by the fact the fragment manager is no
// longer attached.
fm.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
mFragmentManager.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}

super.onDetachedFromWindow();
}

@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks, false);
mFragmentManager.registerFragmentLifecycleCallbacks(mLifecycleCallbacks, false);
}

@Override
Expand Down Expand Up @@ -230,8 +228,8 @@ private void setupBackHandlerIfNeeded(ScreenStackFragment topScreen) {
// notified when it gets resumed so that we can install the handler.
return;
}
getFragmentManager().removeOnBackStackChangedListener(mBackStackListener);
getFragmentManager().popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
mFragmentManager.removeOnBackStackChangedListener(mBackStackListener);
mFragmentManager.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
ScreenStackFragment firstScreen = null;
for (int i = 0, size = mStack.size(); i < size; i++) {
ScreenStackFragment screen = mStack.get(i);
Expand All @@ -241,14 +239,14 @@ private void setupBackHandlerIfNeeded(ScreenStackFragment topScreen) {
}
}
if (topScreen != firstScreen && topScreen.isDismissable()) {
getFragmentManager()
mFragmentManager
.beginTransaction()
.hide(topScreen)
.show(topScreen)
.addToBackStack(BACK_STACK_TAG)
.setPrimaryNavigationFragment(topScreen)
.commitAllowingStateLoss();
getFragmentManager().addOnBackStackChangedListener(mBackStackListener);
mFragmentManager.addOnBackStackChangedListener(mBackStackListener);
}
}
}

0 comments on commit 3fc74e2

Please sign in to comment.