Activity fragment manager problem When change orientation:
Caused by: java.lang.IllegalStateException: Could not find active fragment with index -1
at
android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:3026)
at
android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1446)
at
android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1380)
at
android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
at
com.motors.mobile.core.v2.DaggerIncludeBaseActivity.onCreate(DaggerIncludeBaseActivity.java:26)
Follow my code :
#Override
protected void tabletPortraitInit(Bundle savedInstanceState) {
super.tabletPortraitInit(savedInstanceState);
openSubFragment();
}
#Override
protected void tableLandscapeInit(Bundle savedInstanceState) {
super.tableLandscapeInit(savedInstanceState);
openSubFragment();
}
protected void openSubFragment() {
Bundle bundle = getIntent().getBundleExtra(CAR_DETAIL_KEY);
fragment = new BuyDetailFragment();
if (getSupportFragmentManager().findFragmentByTag(BuyDetailFragment.TAG) != null)
fragment = (BuyDetailFragment) getSupportFragmentManager().findFragmentByTag(BuyDetailFragment.TAG);
fragment.setArguments(bundle);
menuClickListener = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.flMain, fragment, BuyDetailFragment.TAG)
.commit();
// init toolbar items
View tbView = getLayoutInflater().inflate(R.layout.items_detail_menu_layout, toolbar.findViewById(R.id.container), true);
phone = tbView.findViewById(R.id.phone);
message = tbView.findViewById(R.id.message);
link = tbView.findViewById(R.id.notifications);
site = tbView.findViewById(R.id.site);
shortlistView = tbView.findViewById(R.id.wishListMenu);
phone.setOnClickListener((e) -> menuClickListener.clickPhone());
message.setOnClickListener((e) -> menuClickListener.clickMessage());
link.setOnClickListener((e) -> menuClickListener.clickNotifications());
site.setOnClickListener((e) -> menuClickListener.clickSite());
shortlistView.setOnClickListener((e) -> menuClickListener.clickShortlist());
Drawable drawable = ContextCompat.getDrawable(this, R.drawable.vector_heart);
drawable.setColorFilter(ContextCompat.getColor(this, R.color.colorAccent), PorterDuff.Mode.SRC_IN);
shortlistView.changeIcon(drawable);
}
There is no my baseActivity
Does your fragment have setRetainInstance(true)? If so, that may be causing you an issue here, especially if you are using a fragment apart of FragmentStatePagerAdapter.
This can happen with a combination of dismissAllowingStateLoss after onSaveInstanceState and retainInstanceState.
See this helpful example with steps to reproduce (that site does not allow commenting, but it helped me diagnose the issue)
Steps to reproduce:
Open page and show dialog fragment with retainInstance = true
Background app, onSaveInstanceState is called
dismiss dialog in an async task via dismissAllowingStateLoss
perform configuration change, for example by changing language or orientation
open app
crash "Unable to start activity... java.lang.IllegalStateException: Could not find active fragment with index -1"
Under the scenes what's going on is that FragmentManagerImpl.restoreAllState now has an active fragment with an index of -1 because dismissAllowingStateLoss removes the fragment from the backstack, BUT, it is still part of nonConfigFragments because the commit part of dismissAllowingStateLoss was ignored as it was called after onSaveInstanceState.
To fix this will require one of:
not using retainInstanceState on Dialogs that can be dismissed via dismissAllowStateLoss, or
not calling dismiss after state loss
and implementing the desired behavior in a different way.
Related
Here is my code to show DialogFragment from another fragment:
override fun showDialog(dialog: DialogFragment, tag: String?) {
val fragmentManager = fragment.childFragmentManager
if (tag != null) {
fragmentManager.executePendingTransactions()
val fragment = fragmentManager.findFragmentByTag(tag)
if (fragment != null) {
return
}
}
dialog.show(fragmentManager, tag)
}
Everything works well, the dialog is displayed as it should.
However, I observe the following problem:
Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions
at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1636)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1695)
at androidx.fragment.app.FragmentManager.executePendingTransactions(FragmentManager.java:564)
at *******************.showDialog()
Suppose after a network request a dialog was shown through which the user went to the system settings, after returning to the screen of my application, the network request is sent again and the dialog is shown again. And as far as I understand, the fragment transaction is ongoing, and at the same time it tries to execute new transaction in order to show DialogFragment.
As a solution to the problem, I found the use childFragmentManager. But that doesn't help me. Please help me find a possible solution to the problem.
I am using a bottom navigation bar in my MainActivity to handle some fragments. This is the code used for switching between them:
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
if (item.isChecked &&
supportFragmentManager.findFragmentById(R.id.act_main_fragment_container) != null
)
return#OnNavigationItemSelectedListener false
val fragment =
when (item.itemId) {
R.id.navigation_home -> fragments[0]
R.id.navigation_bookings -> fragments[1]
R.id.navigation_messages -> fragments[2]
R.id.navigation_dashboard -> fragments[3]
R.id.navigation_profile -> fragments[4]
else -> fragments[0]
}
this replaceWithNoBackStack fragment
return#OnNavigationItemSelectedListener true
}
the method replaceWithNoBackstack is just a short-hand for this:
supportFragmentManager
?.beginTransaction()
?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
?.replace(containerId, fragment)
?.commit()
The problem is that when i switch faster between them, my app crashes with the following exception:
java.lang.IllegalStateException: Restarter must be created only during owner's initialization stage
at androidx.savedstate.SavedStateRegistryController.performRestore(SavedStateRegistryController.java:59)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2580)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:837)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1237)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1302)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2075)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1865)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1820)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1726)
at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6709)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)
I've been searching a lot and couldn't find an answer.
I also got this error if I do an API call, put the app in background, wait for the response, and at the time I go back to the app, the app crashes because I am trying to display a dialog fragment immediately (the reason I think this is happening is that the transaction of recreating the fragment when coming back from the background is still in progress at the time of displaying the dialog fragment). I solved this in a hacky way by setting a 500ms delay for the dialog because I couldn't figure out other solutions.
Please ask if you need more details regarding this.
Thank you in advance!
POSSIBLE TEMP SOLUTIONS
EDIT
I solved this issue by downgrading the app compat depedency to androidx.appcompat:appcompat:1.0.2 but this is just a temporary solution, since i will have to update it in future. I'm hoping someone will figure it out.
EDIT 2
I solved the issue by removing setTransition() from fragment transactions. At least I know the reason why android apps does not have good transitions in general
EDIT 3
Maybe the best solution to avoid this issue and also make things work smoothly is just to use ViewPager to handle bottom bar navigation
because the version 1.0.0 has not check the state, so it will not throw the exception,
but the version 1.1.0 changes the source code,so it throws the exception.
this is the Fragment version-1.1.0 source code, it will invoke the method performRestore
void performCreate(Bundle savedInstanceState) {
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
mState = CREATED;
mCalled = false;
mSavedStateRegistryController.performRestore(savedInstanceState);
onCreate(savedInstanceState);
mIsCreated = true;
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onCreate()");
}
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
/**
the exception
**/
public void performRestore(#Nullable Bundle savedState) {
Lifecycle lifecycle = mOwner.getLifecycle();
if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
throw new IllegalStateException("Restarter must be created only during "
+ "owner's initialization stage");
}
lifecycle.addObserver(new Recreator(mOwner));
mRegistry.performRestore(lifecycle, savedState);
}
this is the version-1.0.0 source code,did not invoke the performRestore,so will not throw the exception
void performCreate(Bundle savedInstanceState) {
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
mState = CREATED;
mCalled = false;
onCreate(savedInstanceState);
mIsCreated = true;
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onCreate()");
}
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
There are two different solution which can handle this:
The first solution is to split the transaction。
Because we always use replace or merge remove and add into one Transaction.
We can split the transaction to two transaction like this:
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag(tag);
if (prev != null) {
//commit immediately
ft.remove(prev).commitAllowingStateLoss();
}
FragmentTransaction addTransaction = manager.beginTransaction();
addTransaction.addToBackStack(null);
addTransaction.add(layoutId, fragment,
tag).commitAllowingStateLoss();
because this two transaction will be two different Message which will be handled by Handler.
The second solution is check the state in advance.
we can follow the source code,check the state in advance
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag(tag);
if (prev != null) {
if (prev.getLifecycle().getCurrentState() != Lifecycle.State.INITIALIZED) {
return;
}
ft.remove(prev);
}
I recommend the first way,because the second way is folowing the source code,if the source
code change the code, it will be invalid。
I had the same problem.
val fragment = Account.activityAfterLogin
val ft = activity?.getSupportFragmentManager()?.beginTransaction()
//error
ft?.setCustomAnimations(android.R.anim.slide_in_left,android.R.anim.slide_out_right)0
ft?.replace(R.id.framelayout_account,fragment)
ft?.commit()
Changing the library version did not help.
I solved this by adding the ft?.AddToBackStack(null) line after the ft?.setCustomAnimations () method and that’s it.
Animation works and there are no crashes.
If you're using 'androidx.core:core-ktx:1.0.2',
try changing to 1.0.1
If you're using lifecycle(or rxFragment) and androidx_appcompat:alpha05, try changeing versio.
ex) appcompat : 1.1.0-beta01 or 1.0.2
I think's that it appears as an error when saving the state when the target fragment is reused (onPause-onResume).
I changed implementation to api for androidx.appcompat:appcompat:1.0.2 and its worked for me
If it can help, I have encountered the same issue with a BottomNavigationView and setCustomAnimations, basically by switching quickly between Fragments, you may end up starting a FragmentTransaction while the previous one has not finished and then it crashes.
To avoid that, I disable the Navigation Bar until the transition is finished. So I have created a method to enable/disable the BottomNavigationView items (disabling the BottomNavigationView itself does not disable the menu or I didn't find the way) and then I re-enable them once the transition is completed.
To disable the items I call the following method right before starting a FragmentTransition:
public void toggleNavigationBarItems(boolean enabled) {
Menu navMenu = navigationView.getMenu();
for (int i = 0; i < navMenu.size(); ++i) {
navMenu.getItem(i).setEnabled(enabled);
}
}
To re-enable them, I have created an abstract Fragment class for the Fragments loaded from the BottomNavigationView. In this class, I overrides onCreateAnimator (if you use View Animation you should override onCreateAnimation) and I re-enable them onAnimationEnd.
#Nullable
#Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
if(enter){ // check the note below
Animator animator = AnimatorInflater.loadAnimator(getContext(), nextAnim);
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
myActivity.toggleNavigationBarItems(true)
}
});
return animator;
}
return super.onCreateAnimator(transit, enter, nextAnim);
}
Note: as my enter and exit animations have the same duration, I don't need to synchronise them as the enter animation starts after the exit one. That's why the if (enter) is sufficient.
I fixed this problem with add 'synchronized' into add fragment method
before :
public void addFragment(int contentFrameId, Fragment fragment, Bundle param, boolean addToStack) {
try {
if (!fragment.isAdded()) {
if (param != null) {
fragment.setArguments(param);
}
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.add(contentFrameId, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToStack)
fragmentTransaction.addToBackStack(fragment.getClass().toString());
fragmentTransaction.commit();
}
} catch (IllegalStateException e) {
handleError(e.getMessage());
} catch (Exception e) {
handleError(e.getMessage());
}
}
after :
public synchronized void addFragment(int contentFrameId, Fragment fragment, Bundle param, boolean addToStack) {
try {
if (!fragment.isAdded()) {
if (param != null) {
fragment.setArguments(param);
}
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.add(contentFrameId, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToStack)
fragmentTransaction.addToBackStack(fragment.getClass().toString());
fragmentTransaction.commit();
}
} catch (IllegalStateException e) {
handleError(e.getMessage());
} catch (Exception e) {
handleError(e.getMessage());
}
}
This bug seems to be resolved using androidx.appcompat:appcomat:1.1.0-rc01 and androidx.fragment:fragment:1.1.0-rc03
https://developer.android.com/jetpack/androidx/releases/fragment#1.1.0-rc03
I have this issue when using setCustomAnimations.
by removing setCustomAnimations solved my problem.
also I have no problem when I create new instance of fragment before showing it even using setCustomAnimation.
EDIT: another way is adding fragment to backstack.
I was able to fix this (hopefully 😃) by using commitNow() instead of commit() for all bottom nav fragment transactions.
I like this approach better as it allows you to still use custom transitions between fragments.
Note: This is a solution only if you don't want your bottom nav transactions to be added to backstack (which you should not be doing anyways).
Nothing worked except Drown Coder's solution, but it was still not perfect, because it adds transactions to backstack. So if you press all buttons in bottom navigation, you have at least 1 of every fragment in backstack. I slightly improved this solution, so you don't use .replace() that crashes app whith thansaction animations.
Here is the code:
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
getChildFragmentManager().popBackStack();
}
FragmentTransaction addTransaction = getChildFragmentManager().beginTransaction();
addTransaction.setCustomAnimations(R.animator.fragment_fade_in, R.animator.fragment_fade_out);
addTransaction.addToBackStack(null);
addTransaction.add(R.id.frame, fragment, fragment.getClass().getName()).commitAllowingStateLoss();
I found another way of creating this case.
CASE-1
Inflate a fragment in frame-layout at an activity
start an API request (don't consume the api response when app in foreground)
Keep your app in background
Consume the API request (suppose you want to add another fragment on api response)
Inflate another fragment using .replace() method on the same frame-layout
You will be able to create the Crash
CASE-2
Inflate a fragment in frame-layout at an activity
Start an API request
Consume the api in foreground (suppose you want to add another fragment on api response, using .replace() method of fragment-manager)
Put your app in background
Recreate your application (you can do this using "Don't keep activities", changing permission, changing system language)
Come back to your application
Your activity will start re-creating
Activity will auto recreate its already inflated fragment suppose it is of (point-1)
Make sure API is request again in on recreate case, after point-8
Consume API response and inflate another fragment using .replace() method
You will be able to create the Crash (As in this case, already a transition is running point-8, and you are adding another fragment at point-10)
I am trying to get a better understanding of FragmentManager and FragmentTransactions to properly develop my application. It is specifically in regards to their lifecycle, and the long-term effect of committing a FragmentTransaction(add). The reason I have a confusion over it is when I ran a sample Activity, listed at the end of the post.
I purposely created a static FragmentManager variable called fragMan and initially set it to null. It is then checked against in onCreate() if it is null and when null value is seen, the fragMan variable is set to the getFragmentManager() return value. During a configuration change, the Log.d showed that fragma was not null, but the Fragment "CameraFragment" previously added was not found in fragman and the fragman.isDestroyed() returned true. This to me meant that the Activity.getFragmentManager() returns a new instance of a FragmentManager, and that the old FragmentManager instance in fragMan had its data wiped(?)
Here is where the confusion comes in.
1) How is "CameraFragment" still associated in the Activity on a configuration change and is found in
the new instance of FragmentManager?
2) When I hit the back button on my phone to exit the Activity, I then relaunched the sample
Activity using the Apps menu. The CameraFragment was not visible anymore, and the
onCreate() check revealed that fragMan was still not null. I thought that hitting the back button
called the default finish() command, clearing the Activity from memory and that restarting it
would produce the same result as the initial launch of the sample Activity?
Thank you for any and all help you can provide!
public class MainActivity extends Activity
{
static FragmentManager fragMan = null;
FragmentTransaction fragTransaction;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (fragMan != null)
{
Log.d("log", Boolean.toString(fragMan.isDestroyed()));
if(fragMan.findFragmentByTag("Camera Preview") == null)
{
Log.d("log", "Camera Preview not found.");
}
}
else
{
fragMan = getFragmentManager();
fragTransaction = fragMan.beginTransaction();
Fragment cameraFragment = new CameraFragment();
ViewGroup root_view = (ViewGroup) findViewById(android.R.id.content);
fragTransaction.add(root_view.getId(), cameraFragment, "Camera Preview");
fragTransaction.commit();
}
Static variables in Java are kept across Activity creation/destruction - they are associated with the class itself but not a particular instance of the class.
See the official documentation here:
http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
Your application doesn't end when the user returns to the home screen, it just gets put in a background state. If you force stopped the application and restarted it, then the static FragmentManager will be null.
With regards to CameraFragment, unless you've set setRetainInstance(true), it will get destroyed on an orientation change.
==== EDIT
Here's a more detailed flow of what's happening...
You open the application up for the first time
Activity, say instance A1, gets created and its corresponding FragmentManager instance, FM1, also gets created
You store FM1 as a static variable
You go back to home
Activity A1 and FM1 gets destroyed because of the normal Activity lifecycle, although FM1's reference is still held onto by the static variable. At this point, FM1 loses all the fragments it contains and isDestroyed() will return true.
Starting the app again
New Activity instance A2 gets created along with its new FragmentManager instance FM2
My activity invokes the camera with the ACTION_IMAGE_CAPTURE intent. If the camera activity returns succesfully, I set a flag in the onActivityResult callback, and based on the value of the flag I start a fragment in my onResume to add a caption to the image that was captured. This seems to work ok.
I just got a stack trace from the "wild" complaining that I was trying to commit a fragment transaction after onSaveInstanceState has been called. But I'm doing the commit in my onResume method! Why would android complain about this? I do have android:configChanges="orientation|keyboardHidden|keyboard|screenSize" set in my AndroidManifest.xml, so an orientation change should not trigger this....
This occurred on a Samsung Galaxy S3 (SGH-i747) running 4.0.4
Here is the stack:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1325)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:532)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.performFragmentTransition(AddPhotosActivity2.java:278)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.switchToCaptionsFragment(AddPhotosActivity2.java:438)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.onResume(AddPhotosActivity2.java:167)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1158)
at android.app.Activity.performResume(Activity.java:4544)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2448)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2486)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1187)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4514)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
Any help or wisdom is appreciated.
I think I know the answer - I'm using the FragmentActivity from v4 compatibility library, and so I need to perform my fragment transactions in onResumeFragments instead of in onResume. Can someone confirm?
you can use the method commitAllowingStateLoss()
but be aware you can lose the state of your activity
as you can see in google's android reference
which explain the different between the two in the following way
Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.
from my experience it may cause the addToBackStack method not to work sometimes so you will need to add it manually on the fragment
and of course the state won't be saved (textbox text ext.)
this worked for me... found this out on my own... hope it helps you!
1) do NOT have a global "static" FragmentManager / FragmentTransaction.
2) onCreate, ALWAYS initialize the FragmentManager again!
sample below :-
public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
}
protected void setDefaultFragments() {
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null) {
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
}
}
Update I think I have found an explanation and a solution here: http://code.google.com/p/android/issues/detail?id=23096#c4
I implemented the Empty Fragment Workaround posted there and got no more IllegalStateException so far.
I add the invisible state fragment in my activity like this;
#Override
protected void onCreate(final Bundle args) {
...
if (args == null) {
final FragmentManager fm = this.getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
final Fragment emptyFragmentWithCallback = new EmptyFragmentWithCallbackOnResume();
ft.add(emptyFragmentWithCallback, EmptyFragmentWithCallbackOnResume.TAG);
ft.commit();
}
The following code is taken from above link:
public class EmptyFragmentWithCallbackOnResume extends Fragment {
OnFragmentAttachedListener mListener = null;
#Override
public void onAttach(SupportActivity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentAttachedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
}
}
#Override
public void onResume() {
super.onResume();
if (mListener != null) {
mListener.OnFragmentAttached();
}
}
public interface OnFragmentAttachedListener {
public void OnFragmentAttached();
}
}
and invoke fragment transactions that would intuitively go into onResume or onResumeFragments into my custom onFragmentAttached method that is invoked by the invisible state fragment. I do not use onResumeFragments at all and do not issue any fragment transactions in onResume.
So, to sum it up. If you are using the support lib and fragments, pretty much forget about onResume, forget about onResumeFragments and implement your own "onResume" based on the above workaround.
This is somewhat ridiculous.
I cannot confirm. I have the exact same problem. even though I am issuing fragment transaction in onResumeFragments.
This used to work as I posted here: IllegalStateException - Fragment support library .
It seems the error only occurs on 4.0.3 and 4.0.4. However it does neither occur always nor in my Emulator.
I am using support lib rev. 10 and API 16.
I call DialogFragment.show in onResumeFragments and continously get this ridiculous exception from some random users. I cannot reproduce it locally.
I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:
My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.
What I made is next:
Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
I first develop my app targeting android 2.2 (SDK 8), using the support v4 library, and when I start using it with 4.2 (SDK 17), I had the same troubles with my fragments. But changing my Manifest to android:minSdkVersion="8" android:targetSdkVersion="17", solved my problems. Maybe this hepls you too.
Add this to your Activity:
#Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
I have a fragment;
MyFragment myFrag = new MyFragment();
I put bundle data to this fragment:
Bundle bundle = new Bundle();
bundle.putString("TEST", "test");
myFrag.setArguments(bundle);
Then, I replace old fragment with this one and put on backstack:
//replace old fragment
fragmentTransaction.replace(R.id.fragment_placeholder, myFrag, "MyTag");
//put on backstack
fragmentTransaction.addToBackStack(null);
//commit & get transaction ID
int transId = fragmentTransaction.commit();
Later, I pop backstack with the above transaction ID(transId):
//pop the transaction from backstack
fragmentManager.popBackStack(transId,FragmentManager.POP_BACK_STACK_INCLUSIVE);
Later, I set bundle data as argument again to my fragment(myFrag):
//Got Java.lang.IllegalStateException: fragment already active
myFrag.setArguments(bundle);
As you see, my above code got exception Java.lang.IllegalStateException: fragment already active . I don't understand why myFrag is still active though I have popped the transaction of it from backstack., anyhow, since I got the exception I thought I have no choice but de-active the fragment, So, I did:
Fragment activeFragment = fragMgr.findFragmentByTag("MyTag");
fragmentTransaction.remove(activeFragment);
I am not sure if my above code really can de-active the fragment, since I didn't find how to de-active an fragment. :(
After that, when I try to set bundle data to my fragment myFrag again, I still got the same error:
Java.lang.IllegalStateException: fragment already active
Seems even I removed the fragment, it is still active...Why? How to de-active a fragment?
Reading the setArguments(Bundle args) source will help you understand:
/**
* Supply the construction arguments for this fragment. This can only
* be called before the fragment has been attached to its activity; that
* is, you should call it immediately after constructing the fragment. The
* arguments supplied here will be retained across fragment destroy and
* creation.
*/
public void setArguments(Bundle args) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mArguments = args;
}
You cannot use setArguments(Bundle args) again in your code on the same Fragment. What you want to do I guess is either create a new Fragment and set the arguments again. Or you can use getArguments() and then use the put method of the bundle to change its values.
Try removing the previous fragment before adding the new one: https://stackoverflow.com/a/6266144/969325
remove() change fragment status to de-actiive. In your case, you just didn't call commit() after remove(..).
fragmentTransaction.remove(activeFragment);
You would do commit() after remove(), too.
fragmentTransaction.remove(activeFragment).commit();
Had the same issue. I was adding the fragment to backstack. And the error was because I didn't call popbackstack(). Using popbackstack helped me
I'm running into the same issue on Xamarin.android. Here's what the documentation says.
This can only be called before the fragment has been attached to its activity
Just call public method from fragment
if(userFragment==null){
userFragment = new UserFragment();
Bundle bundle = new Bundle();
bundle.putString(Constants.EXTRA_CUSTOMER, result);
userFragment.setArguments(bundle);
}else{
try {
Customer customer = new Customer();
customer.parseCustomer(new JSONObject(result));
userFragment.updateVeiw(customer);
} catch (JSONException e) {
e.printStackTrace();
}
}
First I start with describing why this happens and then I'll come up with the solution I found working... .
This issue happens when Android is removing the fragment from the stack but is not yet finished with removing. In order to check this, you can use the isRemoving() method of the fragment. If false, i.e. the fragment is not active, you can go on with setting the arguments using setArguments(bundle). Otherwise, you can't set arguments to an already active fragment and can only override it by addressing the same arguments using getArguments().putAll(bundle).
To summarize,
if (myFrag.isRemoving()) {
myFrag.getArguments().putAll(bundle);
} else {
myFrag.setArguments(bundle);
}
If you want to avoid this, i.e. removing the fragment at once so there is no active fragment, you might want to use onBackPressed() in onBackStackChangedListener(), which will set the isRemoving() to false.
Check whether your layout current one or old one for example
setContentView(R.layout.activity_main);
Delete old .gradle file in your project file and rebuild gradle file for project.