将 EventBus 与 ViewPager 一起使用时,有时会出现“已添加片段”

Kai*_*ser 7 java android event-bus android-fragments

单击按钮后,我正在使用 EventBus 从一个片段导航到另一个片段。有时且不可重现,会出现以下错误:

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)
Run Code Online (Sandbox Code Playgroud)

这个问题有一些答案,但自从我使用 EventBus 以来,对我没有任何帮助。

这是显然已经添加的片段:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用这个 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在每个问题中,我都有第二个 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我调用打开下一个片段的类:

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

}
Run Code Online (Sandbox Code Playgroud)

我正在使用此调用EventBus.getDefault().post(new SetQuestionAdapterPosition(1));打开下一个片段。我可以使用以下代码来防止应用程序崩溃吗?

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

有什么我可以在我的代码中添加的东西,所以在添加片段之前它会检查它是否已经被添加以防止出现错误语句?

我感谢每一个帮助和建议!

Sha*_*arp 3

EventBus 通常会引入意大利面条式代码,这种情况很可能是由于事件在意外时间触发而引起的。您没有发布有关何时EventBus.getDefault().post()发生的完整逻辑,但我强烈建议仔细检查为什么它会两次触发相同的片段。

然而,一个简单(但黑客)的修复方法是在使用Fragment.isAdded()添加片段之前检查片段是否已添加:

public void addFragment(Fragment fragment) {
    if (!fragment.isAdded()) {
        mFragmentList.add(fragment);
    }
}
Run Code Online (Sandbox Code Playgroud)