Bundle对象的突变

我正在使用遗留代码,我发现这个函数中有一个不一致的行为:

@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获取非空的Bundle args,但doOpenFragment将获得空的Bundle。 片段通过调用commitAllowingStateLoss()

快速修复可以使用Bundle.clone():

  final Bundle args666 = (Bundle) args.clone(); new Handler().postDelayed(new Runnable() { @Override public void run() { doOpenFragment(fragmentClass, addToBackStack, args666); } }, delay); 

它不会处理所有情况,并且只有在api26中才能使用deepCopy。

  1. 为什么会发生?
  2. 如何解决它?

[ 更新 ]

我玩了@ 帕维尔的解决scheme,事情变得更加怪异

  final Bundle args666 = args != null ? cloneThroughSerialization(args) : args; final Bundle args777 = args != null ? (Bundle) args.clone() : args; 

在这里输入图像说明

[UPDATE2]

其实,这个问题不是用postDelayed调用的。 让我们看看调用堆栈:

在这里输入图像说明

goRightToTheCollectionScreen Bundle创build和打包(没有什么可疑的,之后没有突变)。

我猜,问题的来源在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; } 

Solutions Collecting From Web of "Bundle对象的突变"

  1. 其他一些代码在run()被调用之前修改相同的Bundle。 问题在你的代码中。
  2. 您可以通过序列化深入克隆。

      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; } 

你发布的代码似乎没问题,所以可能你的包在其他地方被修改(即使你的postDelayed延迟为0,可运行的稍后执行,同时也可能修改了包)。 尝试直接执行它没有postDelayed,看看问题是否仍然存在。 你可以发布更多的代码,也许我们可以找出你碰到的那个包。

如果没有其他的帮助,你总是可以从API26复制方法到你的代码并使用它(边缘情况 – 这似乎是一个简单的问题,所以你不应该)

无法对捆绑参数build模为“空”。 使用具有指定参数值的片段调用Handler示例进行简单代码。 如果我build模错误请给我提示

 public class MainActivity extends FragmentActivity { private static final String LOG_TAG = "Main activity"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles) // Create an instance of ExampleFragment final HeadlinesFragment firstFragment = new HeadlinesFragment(); // Add the fragment to the 'fragment_container' FrameLayout getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, firstFragment).commit(); long delay = 0; final boolean addToBackStack = true; final Bundle args666 = new Bundle(); args666.putInt("hi", 666); new Handler().postDelayed(new Runnable() { @Override public void run() { doOpenFragment(firstFragment, addToBackStack, args666); } }, delay); } protected void doOpenFragment(HeadlinesFragment firstFragment, final boolean addToBackStack, final Bundle args){ int value = args.getInt("hi"); Log.d(LOG_TAG, "The value is " + value); } }