I'm working with the legacy code and I found an inconsistent behavior in this function:
@Override
public void openFragment(final Class<? extends BaseFragment> fragmentClass,
final boolean addToBackStack,
final Bundle args)
{
long delay = 0;
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
delay = getResources().getInteger(android.R.integer.config_shortAnimTime) * 2;
}
// FIXME: quick fix, but not all cases
final Bundle args666 = args != null ? (Bundle) args.clone() : null;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
doOpenFragment(fragmentClass, addToBackStack, args666);
}
}, delay);
closeDrawer();
}
protected void doOpenFragment(final Class<? extends BaseFragment> fragmentClass,
final boolean addToBackStack,
final Bundle args)
{
try {
if (getSupportFragmentManager().getBackStackEntryCount() >= 1) {
showNavigationIcon();
}
hideKeyboard();
BaseFragment fragment = createFragment(fragmentClass, args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
fragment.initTransactionAnimation(transaction);
String tag = getTag(fragment);
transaction.add(R.id.container, fragment, tag);
if (addToBackStack) {
transaction.addToBackStack(tag);
}
transaction.commitAllowingStateLoss();
hideLastFragment(0);
} catch (Exception e) {
Sentry.captureException(e, "Error opening fragment");
}
}
openFragment
gets non-empty Bundle args, but doOpenFragment
will get empty Bundle. Fragments are committed by calling commitAllowingStateLoss()
A quick fix can be to use Bundle.clone():
final Bundle args666 = (Bundle) args.clone();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
doOpenFragment(fragmentClass, addToBackStack, args666);
}
}, delay);
It will not handle all cases and deepCopy
is available only in api26.
- Why does it happen?
- How to fix it?
[UPDATE]
I played with @Pavel's solution and things get weirder
final Bundle args666 = args != null ? cloneThroughSerialization(args) : args;
final Bundle args777 = args != null ? (Bundle) args.clone() : args;
[UPDATE2]
Actually, the problem isn't with postDelayed
call. Let's see the call stack:
in goRightToTheCollectionScreen
the Bundle is created and packed (nothing suspicious, no mutation afterward).
I guess, the source of the problem in two calls inside openFragmentsChain
:
public void openRootFragmentsChain(Class<? extends BaseFragment> fragmentClass,
List<Class<? extends BaseFragment>> fragmentClasses,
boolean addToBackStack,
Bundle args)
{
openFragmentsChain(fragmentClasses, addToBackStack, args);
openFragment(fragmentClass, true, args);
}
public void openFragmentsChain(List<Class<? extends BaseFragment>> fragmentClasses,
boolean addToBackStack,
Bundle args)
{
try {
for (int i = 0; i < fragmentClasses.size(); i++) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
BaseFragment fragment = createFragment(fragmentClasses.get(i), args);
String tag = getTag(fragment);
transaction.add(R.id.container, fragment, tag);
if (addToBackStack) {
transaction.addToBackStack(tag);
}
if (i != fragmentClasses.size() - 1) {
transaction.hide(fragment);
}
transaction.commitAllowingStateLoss();
}
if (fragmentClasses.size() >= 1) {
updateDrawer();
}
} catch (Exception e) {
Sentry.captureException(e, "Error opening fragment chain");
}
}
protected void doOpenFragment(final Class<? extends BaseFragment> fragmentClass,
final boolean addToBackStack,
final Bundle args)
{
try {
if (getSupportFragmentManager().getBackStackEntryCount() >= 1) {
showNavigationIcon();
}
hideKeyboard();
BaseFragment fragment = createFragment(fragmentClass, args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
fragment.initTransactionAnimation(transaction);
String tag = getTag(fragment);
transaction.add(R.id.container, fragment, tag);
if (addToBackStack) {
transaction.addToBackStack(tag);
}
transaction.commitAllowingStateLoss();
hideLastFragment(0);
} catch (Exception e) {
Sentry.captureException(e, "Error opening fragment");
}
}
protected BaseFragment createFragment(Class<? extends BaseFragment> fragmentClass, Bundle args) throws Exception {
BaseFragment fragment = fragmentClass.newInstance();
fragment.setHasOptionsMenu(true);
fragment.setArguments(args);
fragment.setNavigationHandler(BaseFragmentNavigatorActivity.this);
fragment.setToolbar(mToolbar);
fragment.setMenuLoadService(mMenuLoaderService);
return fragment;
}