简体   繁体   English

处理Android碎片交易问题的正确/最佳方式

[英]Proper/Best Way to Handle Android Fragment Transaction Issue

In my open source Android app, an issue has been discovered where a specific fragment will come into view on top of another fragment or crash the app in a certain situation. 在我的开源Android应用程序中,已发现一个问题,即某个特定片段将在另一个片段之上进入视图或在某种情况下使应用程序崩溃。

The issue on GitHub if you want to see more information and example screenshots: https://github.com/rpi-mobile/RPIMobile-Android/issues/31 如果您想查看更多信息和示例屏幕截图,请访问GitHub: https//github.com/rpi-mobile/RPIMobile-Android/issues/31

I have pinned down the reason, but want to know which of the methods in the android.support.v4.app package to use to resolve the issue. 我已经确定了原因,但想知道android.support.v4.app包中的哪些方法用于解决问题。

In MainActivity.java , is the code for the navigation drawer that uses FragmentTransaction.replace() to switch fragments. MainActivity.java ,是导航抽屉的代码,它使用FragmentTransaction.replace()来切换片段。

The issue arises because in MapFragment , I use: 问题出现了,因为在MapFragment ,我使用:

ViewMapFragment vmf = new ViewMapFragment();
FragmentTransaction ft = getSherlockActivity().getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.content_frame, vmf);
ft.commit();

And in ViewMapFragment 's onDestroyView() : ViewMapFragmentonDestroyView()

FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fm.findFragmentById(R.id.mapview));
ft.commit();

The onDestroyView() properly removes the ViewMapFragment from view, but if you have it in view and use the navigation drawer to change to a different fragment, MapFragment is still on the back stack. onDestroyView()从视图中正确删除ViewMapFragment ,但如果您在视图中使用它并使用导航抽屉更改为其他片段,则MapFragment仍在后台堆栈中。

So, for my questions: 所以,对于我的问题:

1) How do I check if a particular fragment is on the back stack before attempting to remove/replace it, or will the app not crash if you attempt when nothing is there (ie not checking)? 1)如何在尝试删除/替换特定片段之前检查特定片段是否在后端堆栈上,或者如果您尝试什么也没有(即不检查),应用程序是否会崩溃? Eg calling popBackStack() when there is nothing on the back stack. 例如,当后台堆栈上没有任何内容时调用popBackStack()

2) Should I use FragmentManager class methods to attempt to remove the MapFragment from the back stack or should I use FragmentTransaction methods? 2)我应该使用FragmentManager类方法尝试从后栈中删除MapFragment还是应该使用FragmentTransaction方法? Pros vs cons? 优点与缺点?

3) What is the difference in the UI between popBackStack() and popBackStackImmediate() ? 3) popBackStack()popBackStackImmediate()之间的UI有什么区别? Does the user see some glitchy transitions? 用户是否看到了一些毛病的过渡?

According to the documentation of FragmentTransaction , when you invoke addToBackStack method it just remembers what actions you perform in that transaction. 根据FragmentTransaction的文档,当您调用addToBackStack方法时,它只会记住您在该事务中执行的操作。 When popBackStack method is invoked it will reverse those actions and execute them. 当调用popBackStack方法时,它将反转这些操作并执行它们。

So, what happens: 那么,会发生什么:

  1. When we go from MapFragment to ViewMapFragment, FragmentManager remembers removing MapFragment and adding ViewMapFragment operations. 当我们从MapFragment转到ViewMapFragment时,FragmentManager会记住删除MapFragment并添加ViewMapFragment操作。
  2. Then we go to any other fragment using Navigation Drawer, which causes ViewMapFragment removing operation, and after that it adds fragment selected from Drawer. 然后我们使用导航抽屉转到任何其他片段,导致ViewMapFragment删除操作,然后添加从抽屉中选择的片段。
  3. Finally, when we press Back button, popBackStackImmediate is invoked and FragmentManager executes reversed operations: ViewMapFragment is removed (actually it is removed already) and MapFragment is added. 最后,当我们按Back按钮时,调用popBackStackImmediate并且FragmentManager执行反向操作:ViewMapFragment被删除(实际上它已被删除)并添加了MapFragment。 And here is the problem occurs - the selected fragment is still there, so we have two fragments on the screen at the same time. 问题出现了 - 所选片段仍然存在,所以我们在屏幕上同时有两个片段。

There is several options to handle such a situation: 有几种方法可以处理这种情况:

  1. Simply add Navigation Drawer operations to back stack. 只需将导航抽屉操作添加到后台堆栈。
  2. Clear back stack every time when fragment switching is started by Navigation Drawer. 每次导航抽屉启动片段切换时清除堆栈。

To clear back stack you can use this code ( related question ): 要清除堆栈,您可以使用此代码( 相关问题 ):

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

If you put this line in the beginning of your selectItem method the bug will be fixed. 如果将此行放在selectItem方法的开头,则会修复错误。

And your questions: 还有你的问题:

  1. You can check whether a fragment is in a back stack by using FragmentManager.findFragmentByTag method. 您可以使用FragmentManager.findFragmentByTag方法检查片段是否在后栈中。 See example in the end of my answer. 请参阅我的答案末尾的示例。
  2. An entry of back stack technically can contain several fragments, and I don't think there is a way to remove single fragment from it. 从技术上讲,后端堆栈的条目可以包含多个片段,我认为没有办法从中删除单个片段。 Instead of removing you can just clear back stack (please, refer this question ). 而不是删除你可以只清除堆栈(请参阅此问题 )。
  3. The main difference is that popBackStack will give you a chance to do something, before it actually starts popping process ( it will be started when application returns to it's event loop ). 主要区别在于popBackStack会让你有机会在实际开始弹出进程之前做一些事情( 当应用程序返回到它的事件循环时它将启动 )。

Example. 例。 For instance, we have added a fragment with tag "fragment-1": 例如,我们添加了一个标签为“fragment-1”的片段:

getSupportFragmentManager()
        .beginTransaction()
        .replace(R.id.frame, new TestFragment(), "fragment-1")
        .commit();

Then, we put it into a back stack and replace it with another fragment: 然后,我们将它放入后台堆栈并用另一个片段替换它:

getSupportFragmentManager()
        .beginTransaction()
        .addToBackStack(null)
        .replace(R.id.frame, new TestFragment(), "another-fragment")
        .commit();

At this point getSupportFragmentManager().findFragmentByTag("fragment-1") returns our first fragment (it gets it from a back stack entry). 此时,getSupportFragmentManager()。findFragmentByTag(“fragment-1”)返回我们的第一个片段(它从后栈条目中获取)。 Now we can check whether this fragment is added to its activity by isAdded method - if it returns false, then we can make an assumption that the fragment is in a back stack. 现在我们可以通过isAdded方法检查这个片段是否被添加到它的活动中 - 如果它返回false,那么我们可以假设片段在后面的堆栈中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM