“Fragment already added” 有时在使用 EventBus 与 ViewPager 时会发生。

huangapple go评论156阅读模式
英文:

"Fragment already added" occurs sometimes while using EventBus with ViewPager

问题

以下是翻译好的内容:

我在按钮点击后使用EventBus从一个片段导航到另一个片段。有时出现以下错误,但无法重现:

致命异常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)

对于这个问题有一些答案,但是对我来说都没有用,因为我正在使用EventBus。

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

public class FragmentQuestion3_2 extends BaseFragment {

    // ... 其他成员变量和方法 ...

}

我正在使用这个FragmentStatePagerAdapter:

public class QuestionsPagerAdapterRound2 extends FragmentStatePagerAdapter {
    
    // ... 其他成员变量和方法 ...

}

在每个问题中,我有一个第二个ViewPager,因此在一个ViewPager中有另一个ViewPager,这是第二个ViewPager:

public class ViewPagerOptions3Round2 extends FragmentStatePagerAdapter {

    // ... 其他成员变量和方法 ...

}

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

public class SetQuestionAdapterPosition {

    // ... 其他成员变量和方法 ...

}

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

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

在添加片段之前,我是否可以在我的代码中添加一些内容,以便检查是否已经添加了片段以防止出现错误语句?

我感谢所有的帮助和建议!

英文:

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!

答案1

得分: 3

EventBus经常会引入混乱的代码,这种情况很可能是由于事件在意外的时间触发导致的。你没有贴出EventBus.getDefault().post()何时发生的完整逻辑,但我强烈建议仔细检查为什么它会触发相同的片段两次。

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

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

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

huangapple
  • 本文由 发表于 2020年7月27日 22:35:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/63117607.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定