当 activity 从后台到前台时,重用片段会导致 IllegalArgumentException
when activity is background to foreground, reuse fragment cause IllegalArgumentException
我的activity使用预初始化的片段(初始化onCreate)
当片段 (A) 首先显示时,使用 FragmentTransaction.add(A)。
如果片段 B 请求显示(如果它是第一个),FragmentTransaction.detach(A) 和 FragmentTransaction.add(B).
片段A再次请求显示,使用FragmentTransaction.detach(B)和
FragmentTransaction.attach(A).
此操作在 BottomNavagationView.OnNavigationItemSelectedListener.
在这种情况下,我使应用程序完成(使用后退按钮,但不使用 activity.finish),然后再次 运行 该应用程序,不显示任何片段(期望显示片段 B ).
并且,当我单击 BottomNavagationView 按钮(片段添加)时,
原因java.lang.IllegalStateException:在 onSaveInstanceState at commit() 后无法执行此操作。
如何解决这个问题?
commitallowingstateloss() 似乎无法正常工作...
附上我的代码:
public class TestActivity extends AppCompatActivity {
private TextView mTextMessage;
private BottomNavigationView navigation;
private Fragment homeFragment = null;
private Fragment seatFragment = null;
private Fragment settingFragment = null;
private Fragment dialogFragment = null;
private FragmentUtil fUtil = null;
private boolean finishFlag = false;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fUtil.addOnMain(R.id.content, homeFragment, "home", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
case R.id.navigation_seat:
fUtil.addOnMain(R.id.content, seatFragment, "seat", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
case R.id.navigation_shelf:
return true;
case R.id.navigation_settings:
fUtil.addOnMain(R.id.content, settingFragment, "setting", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// mTextMessage = (TextView) findViewById(R.id.message);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
BottomNavigationViewHelper.disableShiftMode(navigation);
fUtil = FragmentUtil.getInstance(this);
homeFragment = new HomeFragment();
seatFragment = new SeatMenuFragment();
settingFragment = new SettingFragment();
dialogFragment = new DialogFragment();
findViewById(R.id.login_popup).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(fUtil.isContained(dialogFragment)) {
return;
} else {
fUtil.add(R.id.content, dialogFragment, "", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
}
});
}
和 FragmentUtil:
public class FragmentUtil {
private Context context = null;
private FragmentManager manager = null;
private static FragmentUtil sInstance = null;
private Fragment currentFragment = null;
public static FragmentUtil getInstance(FragmentActivity activity) {
if (sInstance == null) {
sInstance = new FragmentUtil(activity);
}
return sInstance;
}
private FragmentUtil(FragmentActivity activity) {
this.context = activity.getApplicationContext();
this.manager = activity.getSupportFragmentManager();
}
public FragmentManager getManager() {
return manager;
}
private void setTransition(FragmentTransaction ft, int transition) {
ft.setTransition(transition);
}
public void attach(Fragment fragment) {
if(fragment.isDetached()) {
FragmentTransaction ft = manager.beginTransaction();
ft.attach(fragment);
ft.commit();
currentFragment = fragment;
} else {
return;
}
}
public void attach(Fragment fragment, int transition) {
if(fragment.isDetached()) {
FragmentTransaction ft = manager.beginTransaction();
ft.attach(fragment);
if (transition != FragmentTransaction.TRANSIT_UNSET) {
setTransition(ft, transition);
}
ft.commit();
currentFragment = fragment;
} else {
return;
}
}
public void detach(Fragment fragment, int transition) {
if(fragment.isDetached()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.detach(fragment);
if(transition != FragmentTransaction.TRANSIT_UNSET) {
setTransition(ft, transition);
}
currentFragment = null;
ft.commit();
}
}
public void detach(Fragment fragment) {
if(fragment.isDetached()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.detach(fragment);
ft.commit();
currentFragment = null;
}
}
public void add(int resId, Fragment fragment, String tag) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.addToBackStack(null);
ft.add(resId, fragment, tag);
ft.commit();
currentFragment = fragment;
}
}
public void addOnMain(int resId, Fragment fragment, String tag) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = manager.beginTransaction();
ft.add(resId, fragment, tag);
ft.commit();
currentFragment = fragment;
}
}
public void remove(Fragment fragment) {
//manager.executePendingTransactions();
manager.popBackStack();
FragmentTransaction ft = manager.beginTransaction();
ft.remove(fragment);
ft.commit();
}
public void add(int resId, Fragment fragment, String tag, int transition) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.addToBackStack(null);
ft.add(resId, fragment, tag);
setTransition(ft, transition);
ft.commit();
currentFragment = fragment;
}
}
public void addOnMain(int resId, Fragment fragment, String tag, int transition) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = manager.beginTransaction();
ft.add(resId, fragment, tag);
setTransition(ft, transition);
ft.commit();
currentFragment = fragment;
}
}
public void remove(Fragment fragment, int transition) {
//manager.executePendingTransactions();
FragmentTransaction ft = manager.beginTransaction();
ft.remove(fragment);
setTransition(ft, transition);
ft.commit();
}
HomeFragment 和其他什么都不做。只是膨胀 xml.
我目前正在处理一个带有 BottomNavigationView 和 Fragments 的项目。基本上这就是我做事的方式(为简单起见,省略了一些代码)。
MainActivity.java:
public class MainActivity extends AppCompatActivity
implements OnFragmentSelectedListener {
// the MainFragment in Fragment container with id R.id.fragment_container_main and state "started"
private MainFragment selectedFragment;
// MainFragments
private HomeFragment homeFragment;
private SeatFragment seatFragment;
private SettingFragment settingFragment;
private DialogFragment dialogFragment;
private BottomNavigationView navBar;
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
/*
* Navigation strategy:
* - if the Fragment being selected on the NavBar is already visible, ignore the click
* - else navigate to the requested Fragment, creating it if necessary.
*/
switch (item.getItemId()) {
case R.id.navigation_home:
if (!(selectedFragment instanceof HomeFragment)) {
if (homeFragment == null) {
homeFragment = new HomeFragment();
}
displayFragment(homeFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_seat:
if (!(selectedFragment instanceof SeatFragment)) {
if (seatFragment == null) {
seatFragment = new SeatFragment();
}
displayFragment(seatFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_setting:
if (!(selectedFragment instanceof SettingFragment)) {
if (settingFragment == null) {
settingFragment = new SettingFragment();
}
displayFragment(settingFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_dialog:
if (!(selectedFragment instanceof DialogFragment)) {
if (dialogFragment == null) {
dialogFragment = new DialogFragment();
}
displayFragment(dialogFragment, R.id.fragment_container_main);
}
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navBar = findViewById(R.id.navigation);
navBar.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener);
if (savedInstanceState == null) {
homeFragment = new HomeFragment();
displayFragment(homeFragment, R.id.fragment_container_main);
}
}
/**
* Replaces the fragment (MainFragment) in main fragment container with fragmentToDisplay, adding
* the transaction to the back stack.
*
* @param fragmentToDisplay the fragment to display
*/
@Override
public void displayFragment(MainFragment fragmentToDisplay, int fragmentContainerId) {
FragmentManager fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(fragmentContainerId, fragmentToDisplay, fragmentToDisplay.getClass().getName());
ft.addToBackStack(null);
ft.commit();
}
@Override
public void onBackPressed() {
if (selectedFragment == null || !selectedFragment.onBackPressed()) {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate();
} else {
super.onBackPressed();
}
}
}
@Override
public void setSelectedFragment(MainFragment fragment) {
selectedFragment = fragment;
}
}
MainFragment.java:
/**
* Abstract Fragment class for MainActivity Fragments
* <p>
* Created by kazume on 01.02.2018.
*/
public abstract class MainFragment extends Fragment {
protected OnFragmentSelectedListener onFragmentSelectedListener;
/**
* Called when a back-press occurs in MainActivity popping the back stack (or exiting the app if
* the back stack is empty ) by default. The return boolean offers the possibility to let the
* MainFragment consume the back-press and stay on the MainFragment by returning true.
*
* @return Returns true if MainActivity's back-press is consumed by MainFragment and false if
* back-press is handled by MainActivity
*/
@SuppressWarnings("SameReturnValue")
public boolean onBackPressed() {
return false;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
onFragmentSelectedListener = (OnFragmentSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnFragmentSelectedListener");
}
}
@Override
public void onStart() {
super.onStart();
if (this.getId() == R.id.fragment_container_main) {
onFragmentSelectedListener.setSelectedFragment(this);
}
}
@Override
public void onDetach() {
super.onDetach();
onFragmentSelectedListener = null;
}
}
OnFragmentSelectedListener.java
/**
* Interface to be implemented by an Activity hosting MainFragments. Via this interface, the hosting
* activity always has a reference to the Fragment currently being displayed (state "started") in the
* main fragment container.
*
* Created by kazume on 05.02.2017.
*/
public interface OnFragmentSelectedListener {
void setSelectedFragment(MainFragment fragment);
}
在执行片段的 commit() 之前,您应该检查如下条件。
if(!getSupportFragmentManager().isStateSaved()) { //this to avoid IllegalStateException
//Here do your fragment.commit(); or fragmentDialog.show() etc.
}
refer to this post for more detail
我的activity使用预初始化的片段(初始化onCreate)
当片段 (A) 首先显示时,使用 FragmentTransaction.add(A)。 如果片段 B 请求显示(如果它是第一个),FragmentTransaction.detach(A) 和 FragmentTransaction.add(B).
片段A再次请求显示,使用FragmentTransaction.detach(B)和 FragmentTransaction.attach(A).
此操作在 BottomNavagationView.OnNavigationItemSelectedListener.
在这种情况下,我使应用程序完成(使用后退按钮,但不使用 activity.finish),然后再次 运行 该应用程序,不显示任何片段(期望显示片段 B ).
并且,当我单击 BottomNavagationView 按钮(片段添加)时,
原因java.lang.IllegalStateException:在 onSaveInstanceState at commit() 后无法执行此操作。
如何解决这个问题?
commitallowingstateloss() 似乎无法正常工作...
附上我的代码:
public class TestActivity extends AppCompatActivity {
private TextView mTextMessage;
private BottomNavigationView navigation;
private Fragment homeFragment = null;
private Fragment seatFragment = null;
private Fragment settingFragment = null;
private Fragment dialogFragment = null;
private FragmentUtil fUtil = null;
private boolean finishFlag = false;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fUtil.addOnMain(R.id.content, homeFragment, "home", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
case R.id.navigation_seat:
fUtil.addOnMain(R.id.content, seatFragment, "seat", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
case R.id.navigation_shelf:
return true;
case R.id.navigation_settings:
fUtil.addOnMain(R.id.content, settingFragment, "setting", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// mTextMessage = (TextView) findViewById(R.id.message);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
BottomNavigationViewHelper.disableShiftMode(navigation);
fUtil = FragmentUtil.getInstance(this);
homeFragment = new HomeFragment();
seatFragment = new SeatMenuFragment();
settingFragment = new SettingFragment();
dialogFragment = new DialogFragment();
findViewById(R.id.login_popup).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(fUtil.isContained(dialogFragment)) {
return;
} else {
fUtil.add(R.id.content, dialogFragment, "", FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
}
});
}
和 FragmentUtil:
public class FragmentUtil {
private Context context = null;
private FragmentManager manager = null;
private static FragmentUtil sInstance = null;
private Fragment currentFragment = null;
public static FragmentUtil getInstance(FragmentActivity activity) {
if (sInstance == null) {
sInstance = new FragmentUtil(activity);
}
return sInstance;
}
private FragmentUtil(FragmentActivity activity) {
this.context = activity.getApplicationContext();
this.manager = activity.getSupportFragmentManager();
}
public FragmentManager getManager() {
return manager;
}
private void setTransition(FragmentTransaction ft, int transition) {
ft.setTransition(transition);
}
public void attach(Fragment fragment) {
if(fragment.isDetached()) {
FragmentTransaction ft = manager.beginTransaction();
ft.attach(fragment);
ft.commit();
currentFragment = fragment;
} else {
return;
}
}
public void attach(Fragment fragment, int transition) {
if(fragment.isDetached()) {
FragmentTransaction ft = manager.beginTransaction();
ft.attach(fragment);
if (transition != FragmentTransaction.TRANSIT_UNSET) {
setTransition(ft, transition);
}
ft.commit();
currentFragment = fragment;
} else {
return;
}
}
public void detach(Fragment fragment, int transition) {
if(fragment.isDetached()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.detach(fragment);
if(transition != FragmentTransaction.TRANSIT_UNSET) {
setTransition(ft, transition);
}
currentFragment = null;
ft.commit();
}
}
public void detach(Fragment fragment) {
if(fragment.isDetached()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.detach(fragment);
ft.commit();
currentFragment = null;
}
}
public void add(int resId, Fragment fragment, String tag) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.addToBackStack(null);
ft.add(resId, fragment, tag);
ft.commit();
currentFragment = fragment;
}
}
public void addOnMain(int resId, Fragment fragment, String tag) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = manager.beginTransaction();
ft.add(resId, fragment, tag);
ft.commit();
currentFragment = fragment;
}
}
public void remove(Fragment fragment) {
//manager.executePendingTransactions();
manager.popBackStack();
FragmentTransaction ft = manager.beginTransaction();
ft.remove(fragment);
ft.commit();
}
public void add(int resId, Fragment fragment, String tag, int transition) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
FragmentTransaction ft = manager.beginTransaction();
ft.addToBackStack(null);
ft.add(resId, fragment, tag);
setTransition(ft, transition);
ft.commit();
currentFragment = fragment;
}
}
public void addOnMain(int resId, Fragment fragment, String tag, int transition) {
//manager.executePendingTransactions();
if(fragment.isAdded()) {
return;
} else {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = manager.beginTransaction();
ft.add(resId, fragment, tag);
setTransition(ft, transition);
ft.commit();
currentFragment = fragment;
}
}
public void remove(Fragment fragment, int transition) {
//manager.executePendingTransactions();
FragmentTransaction ft = manager.beginTransaction();
ft.remove(fragment);
setTransition(ft, transition);
ft.commit();
}
HomeFragment 和其他什么都不做。只是膨胀 xml.
我目前正在处理一个带有 BottomNavigationView 和 Fragments 的项目。基本上这就是我做事的方式(为简单起见,省略了一些代码)。
MainActivity.java:
public class MainActivity extends AppCompatActivity
implements OnFragmentSelectedListener {
// the MainFragment in Fragment container with id R.id.fragment_container_main and state "started"
private MainFragment selectedFragment;
// MainFragments
private HomeFragment homeFragment;
private SeatFragment seatFragment;
private SettingFragment settingFragment;
private DialogFragment dialogFragment;
private BottomNavigationView navBar;
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
/*
* Navigation strategy:
* - if the Fragment being selected on the NavBar is already visible, ignore the click
* - else navigate to the requested Fragment, creating it if necessary.
*/
switch (item.getItemId()) {
case R.id.navigation_home:
if (!(selectedFragment instanceof HomeFragment)) {
if (homeFragment == null) {
homeFragment = new HomeFragment();
}
displayFragment(homeFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_seat:
if (!(selectedFragment instanceof SeatFragment)) {
if (seatFragment == null) {
seatFragment = new SeatFragment();
}
displayFragment(seatFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_setting:
if (!(selectedFragment instanceof SettingFragment)) {
if (settingFragment == null) {
settingFragment = new SettingFragment();
}
displayFragment(settingFragment, R.id.fragment_container_main);
}
return true;
case R.id.navigation_dialog:
if (!(selectedFragment instanceof DialogFragment)) {
if (dialogFragment == null) {
dialogFragment = new DialogFragment();
}
displayFragment(dialogFragment, R.id.fragment_container_main);
}
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navBar = findViewById(R.id.navigation);
navBar.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener);
if (savedInstanceState == null) {
homeFragment = new HomeFragment();
displayFragment(homeFragment, R.id.fragment_container_main);
}
}
/**
* Replaces the fragment (MainFragment) in main fragment container with fragmentToDisplay, adding
* the transaction to the back stack.
*
* @param fragmentToDisplay the fragment to display
*/
@Override
public void displayFragment(MainFragment fragmentToDisplay, int fragmentContainerId) {
FragmentManager fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(fragmentContainerId, fragmentToDisplay, fragmentToDisplay.getClass().getName());
ft.addToBackStack(null);
ft.commit();
}
@Override
public void onBackPressed() {
if (selectedFragment == null || !selectedFragment.onBackPressed()) {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate();
} else {
super.onBackPressed();
}
}
}
@Override
public void setSelectedFragment(MainFragment fragment) {
selectedFragment = fragment;
}
}
MainFragment.java:
/**
* Abstract Fragment class for MainActivity Fragments
* <p>
* Created by kazume on 01.02.2018.
*/
public abstract class MainFragment extends Fragment {
protected OnFragmentSelectedListener onFragmentSelectedListener;
/**
* Called when a back-press occurs in MainActivity popping the back stack (or exiting the app if
* the back stack is empty ) by default. The return boolean offers the possibility to let the
* MainFragment consume the back-press and stay on the MainFragment by returning true.
*
* @return Returns true if MainActivity's back-press is consumed by MainFragment and false if
* back-press is handled by MainActivity
*/
@SuppressWarnings("SameReturnValue")
public boolean onBackPressed() {
return false;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
onFragmentSelectedListener = (OnFragmentSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnFragmentSelectedListener");
}
}
@Override
public void onStart() {
super.onStart();
if (this.getId() == R.id.fragment_container_main) {
onFragmentSelectedListener.setSelectedFragment(this);
}
}
@Override
public void onDetach() {
super.onDetach();
onFragmentSelectedListener = null;
}
}
OnFragmentSelectedListener.java
/**
* Interface to be implemented by an Activity hosting MainFragments. Via this interface, the hosting
* activity always has a reference to the Fragment currently being displayed (state "started") in the
* main fragment container.
*
* Created by kazume on 05.02.2017.
*/
public interface OnFragmentSelectedListener {
void setSelectedFragment(MainFragment fragment);
}
在执行片段的 commit() 之前,您应该检查如下条件。
if(!getSupportFragmentManager().isStateSaved()) { //this to avoid IllegalStateException
//Here do your fragment.commit(); or fragmentDialog.show() etc.
}
refer to this post for more detail