I am using EventBus to navigate from one fragment to another after a button click. 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.
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:
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:
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:
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));
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. 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.
However, an easy (but hacky) fix is to check if the fragment was already added before adding it with Fragment.isAdded():
public void addFragment(Fragment fragment) {
if (!fragment.isAdded()) {
mFragmentList.add(fragment);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With