简体   繁体   English

在使用带有 ViewPager 的 EventBus 时,有时会出现“已添加片段”

[英]“Fragment already added” occurs sometimes while using EventBus with ViewPager

I am using EventBus to navigate from one fragment to another after a button click.单击按钮后,我正在使用 EventBus 从一个片段导航到另一个片段。 Sometimes and not reproducible the following error comes up:有时会出现以下错误且不可重现:

Fatal Exception: java.lang.IllegalStateException: Fragment already added: FragmentQuestion3_2{82d0763} (e57d5f9c-c80b-4f99-a4f4-e844bdabdef5) id=0x7f0a0487}
       at androidx.fragment.app.FragmentStore.addFragment(FragmentStore.java:67)
       at androidx.fragment.app.FragmentManager.addFragment(FragmentManager.java:1563)
       at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:405)
       at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
       at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
       at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
       at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
       at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
       at android.os.Handler.handleCallback(Handler.java:873)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:216)
       at android.app.ActivityThread.main(ActivityThread.java:7211)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)

There are some answers to that problem but nothing worked for me since I am using EventBus.这个问题有一些答案,但因为我使用的是 EventBus,所以对我没有任何帮助。

Here is the Fragment that apparently already have been added:这是显然已经添加的片段:

public class FragmentQuestion3_2 extends BaseFragment {

    private ViewModelQuestionAnswer viewModel;
    private String category;
    private ImageView imageCategory;
    private TextView question3;

    PagerAdapter viewPagerAdapter;
    public NonSwipeAbleViewPager optionAnswerViewPager;

    private boolean isFragmentLoaded = false;

    public FragmentQuestion3_2() {
        // Required empty public constructor
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        EventBus.getDefault().register(this);
    }

    public static FragmentQuestion3_2 newInstance(String questionNumber) {
        return new FragmentQuestion3_2();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // init ViewModel
        viewModel = ViewModelProviders.of(requireActivity()).get(ViewModelQuestionAnswer.class);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_question, container, false);

        imageCategory = view.findViewById(R.id.image_view_question);
        question3 = view.findViewById(R.id.question);
        optionAnswerViewPager = view.findViewById(R.id.option_answer_view_pager);

        // Gets the category so the ImageView can be updated accordingly
        viewModel.getCategory().observe(requireActivity(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {

                category = s;

                setUpCategoryImage();

            }
        });

        // Gets the question
        viewModel.getQuestion3().observe(requireActivity(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                question3.setText(s);

            }
        });

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!isFragmentLoaded) {
            isFragmentLoaded = true;
            init();
        }
    }

    private void initOptionAnswerViewPager() {
        viewPagerAdapter = new ViewPagerOptions3Round2(getChildFragmentManager(), 2);
        Utils.getInstance().changePagerScroller(optionAnswerViewPager);
        optionAnswerViewPager.setAdapter(viewPagerAdapter);
        optionAnswerViewPager.setPagingEnabled(false);
        optionAnswerViewPager.setOffscreenPageLimit(3);
    }

    @Override
    protected void setupListeners() {

    }

    @Subscribe
    public void setViewPagerPosition(SetViewPagerPosition setViewPagerPosition) {
        optionAnswerViewPager.setCurrentItem(setViewPagerPosition.getPosition(), true);
    }

    @Override
    public void init() {

        initOptionAnswerViewPager();

    }
    @Override
    public void onDetach() {
        EventBus.getDefault().unregister(this);
        super.onDetach();
    }
}

I am using this FragmentStatePagerAdapter:我正在使用这个 FragmentStatePagerAdapter:

public class QuestionsPagerAdapterRound2 extends FragmentStatePagerAdapter {
    private static final String QUESTION_NUMBER = "question_number";

    private int mNumOfTabs;
    private final List<Fragment> mFragmentList = new ArrayList<>();


    public QuestionsPagerAdapterRound2(FragmentManager fm, int NumOfTabs) {
        super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.mNumOfTabs = NumOfTabs;
    }

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return FragmentCategory_2.newInstance("1");
            case 1:
                return FragmentQuestion1_2.newInstance("2");
            case 2:
                return FragmentQuestion2_2.newInstance("3");
            case 3:
                return FragmentQuestion3_2.newInstance("4");
            case 4:
                return FragmentQuestionResult_2.newInstance("5");

            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return mNumOfTabs;
    }

    @Override
    public int getItemPosition(Object object) {
        return PagerAdapter.POSITION_NONE;
    }
}

Within each question I have a second ViewPager, so there is a ViewPager within a ViewPager, here is the second ViewPager:在每个问题中,我都有第二个 ViewPager,因此 ViewPager 中有一个 ViewPager,这是第二个 ViewPager:

public class ViewPagerOptions3Round2 extends FragmentStatePagerAdapter {

    private int mNumOfTabs;
    private final List<Fragment> mFragmentList = new ArrayList<>();


    public ViewPagerOptions3Round2(FragmentManager fm, int NumOfTabs) {
        super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.mNumOfTabs = NumOfTabs;
    }

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return FragmentOptions3.newInstance();
            case 1:
                return FragmentAnswer3_2.newInstance();
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return mNumOfTabs;
    }

    @Override
    public int getItemPosition(Object object) {
        return PagerAdapter.POSITION_NONE;
    }
}

This is my class I call to open the next fragment:这是我调用的 class 打开下一个片段:

public class SetQuestionAdapterPosition {

    private Integer position;

    public Integer getPosition() {
        return position;
    }

    public void setPosition(Integer position) {
        this.position = position;
    }

    public SetQuestionAdapterPosition(Integer position) {
        this.position = position;
    }

}

I am using this call EventBus.getDefault().post(new SetQuestionAdapterPosition(1));我正在使用这个调用EventBus.getDefault().post(new SetQuestionAdapterPosition(1)); to open the next fragment.打开下一个片段。 Can I use the following code to prevent the app to crash?我可以使用以下代码来防止应用程序崩溃吗?

        try {
            EventBus.getDefault().post(new SetQuestionAdapterPosition(1));
        } catch (Exception e){
            Log.e("Answer2", e.toString());
        }

Is there something I can add in my code so before adding the fragment it checks if its already been added to prevent the error statement?有什么我可以在我的代码中添加的东西,所以在添加片段之前它会检查它是否已经被添加以防止错误语句?

I appreciate every help and suggestion!我感谢每一个帮助和建议!

EventBus can often introduce spaghetti code and this situation is likely caused by the event being triggered at an unintended time. EventBus 经常会引入意大利面条式的代码,这种情况很可能是由于事件在非预期时间触发造成的。 You didn't post the entire logic around when EventBus.getDefault().post() happens, but I strongly recommend double-checking why it triggers the same fragment twice.您没有在EventBus.getDefault().post()发生时发布整个逻辑,但我强烈建议仔细检查为什么它会两次触发相同的片段。

However, an easy (but hacky) fix is to check if the fragment was already added before adding it with Fragment.isAdded() :但是,一个简单(但很麻烦)的修复方法是在使用Fragment.isAdded()添加之前检查片段是否已经添加:

public void addFragment(Fragment fragment) {
    if (!fragment.isAdded()) {
        mFragmentList.add(fragment);
    }
}

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

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