android - Mutation of a Bundle object -


i'm working legacy code , found inconsistent behavior in 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, not 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, doopenfragment empty bundle. fragments committed calling commitallowingstateloss()

a quick fix can use bundle.clone():

    final bundle args666 = (bundle) args.clone();     new handler().postdelayed(new runnable() {         @override         public void run() {             doopenfragment(fragmentclass, addtobackstack, args666);         }     }, delay); 

it not handle cases , deepcopy available in api26.

  1. why happen?
  2. how fix it?

[update]

i played @pavel's solution , things weirder

    final bundle args666 = args != null ? clonethroughserialization(args) : args;     final bundle args777 = args != null ? (bundle) args.clone() : args; 

enter image description here

[update2]

actually, problem isn't postdelayed call. let's see call stack:

enter image description here

in gorighttothecollectionscreen bundle created , packed (nothing suspicious, no mutation afterward).

i guess, source of problem in 2 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 {         (int = 0; < 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; } 

  1. some other code modifies same bundle before run() called. problem in code.
  2. you can deep clone through serialization.

        public static bundle clonethroughserialization(@nonnull bundle bundle) {         parcel parcel = parcel.obtain();         bundle.writetoparcel(parcel, 0);          bundle clonedbundle  = new bundle();         clonedbundle.readfromparcel(parcel);          parcel.recycle();         return clonedbundle;     } 

Comments

Popular posts from this blog

Is there a better way to structure post methods in Class Based Views -

performance - Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? -

jquery - Responsive Navbar with Sub Navbar -