I'm using SupportMapFragment and it's crashing with illegal state exception while committing.
Here's the code below:
mSupportMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.branch_mp_view);
if (mSupportMapFragment == null) {
FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
mSupportMapFragment = SupportMapFragment.newInstance();
fragmentTransaction.replace(R.id.branch_mp_view, mSupportMapFragment).commit();
}
if (mSupportMapFragment != null) {
mSupportMapFragment.getMapAsync(this);
}
Logs:
Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1538)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1556)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:696)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:662)
at com.android.myapp.ui.branch_master.MyFragment.setMap(MyFragment.java:1346)
at com.android.myapp.ui.branch_master.MyFragment.handleSuccessBranchDetail(MyFragment.java:3267)
at com.android.myapp.ui.branch_master.MyFragment$1.onResponse$230aa0b4(MyFragment.java:200)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5637)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
Pls Help, I'm not able to reproduce it but i'm seeing this crash in crashlytics appears once in a while.
It looks like you are trying to show the mapFragment after an apicall completes. In the meantime the user could have already closed the app, or gone to a different screen. When this happens your Activity or Fragment gets paused..After pausing it is not possible anymore to replace fragments. You could prevent this by checking if your ap is still in the foreground. For example when onResume is called you could set a boolean named foreground to true, and to false when onPause is called. You can now use this variable to see if your fragment can be displayed.
Related
When I click the show more button a new fragment opens. However if I rotate the device and then click it I get an error.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.rekijan.initiativetrackersecondedition, PID: 4673
java.lang.IllegalStateException: FragmentManager has been destroyed
at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1878)
at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329)
at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294)
at com.rekijan.initiativetrackersecondedition.ui.activities.MainActivity.replaceCharacterDetailFragment(MainActivity.java:207)
at com.rekijan.initiativetrackersecondedition.character.adapter.CharacterAdapter$2.onClick(CharacterAdapter.java:305)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
So the code is in a RecyclerViewAdapter
holder.showCharacterDetailButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((MainActivity)context).replaceCharacterDetailFragment(holder.getAdapterPosition());
}
});
And then in MainActivity
public void replaceCharacterDetailFragment(int position) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (getResources().getBoolean(R.bool.isTablet)) {
transaction.replace(R.id.second_fragment_container, CharacterDetailFragment.newInstance(position));
transaction.commit();
} else {
transaction.replace(R.id.main_fragment_container, CharacterDetailFragment.newInstance(position));
transaction.addToBackStack(null);
transaction.commit();
//Enable the back button in action bar
if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AppExtension app = (AppExtension) this.getApplicationContext();
app.setShowBackNavigation(true);
}
}
More code can be found in github becuase open source yay https://github.com/ej-krielen/pf2initiativetracker
A common given 'solution' is to use isDestroyed or isFinishing. Sure the app doesn't crash if I use that but then the fragment is also not being made.
So to reiterate:
Fragment goes fine initially but not after rotation.
FragmentManager has been destroyed? How to get it back/properly use it?
Right sometimes when updating an old project you forget your own logic. The problem was that in my adapter I use a reference to context. The adapter however is created in the AppExtension so on a recreate of the Activity this wasn't being set again. So now when the Activity gets recreated I pass the context to the adapter again and it all works fine.
I am trying to remove a fragment but very rarely on Crashlytics I am able to see a bug java.lang.IllegalStateException: Cannot remove Fragment attached to a different FragmentManager.
I have a separate fragment for fetching user's current location. I use below code to open the fragment.
private void openLocationFragment() {
LocationFragment locationFragment = LocationFragment.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, locationFragment, "location_fragment")
.commitAllowingStateLoss();
}
Now In fragment as soon as i get a location update, I call method onLocationFetched using listener attached to the activity.
Inside this method I remove the fragment using below code.
#Override
public void onLocationFetched(Location location) {
FragmentManager fragmentManager = getSupportFragmentManager();
LocationFragment locationFragment = (LocationFragment) fragmentManager.findFragmentByTag("location_fragment");
if (locationFragment != null) {
fragmentManager.beginTransaction()
.remove(locationFragment) // Here is the exception
.commitAllowingStateLoss();
}
if(location == null){
fetchUserDetails();
}else
fetchCity(location);
}
StackTrace:
Fatal Exception: **java.lang.IllegalStateException: Cannot remove Fragment attached to a different FragmentManager.**
Fragment LocationFragment{32a2ed0 (d41fc341-baf2-4266-948a-866fba7e57b5) id=0x7f09028b location_fragment} is already attached to a FragmentManager.
at androidx.fragment.app.BackStackRecord.remove(BackStackRecord.java:316)
at com.avail.easyloans.feature.marketplace.activities.ActivityMarketplace.onFragmentFetched(ActivityMarketplace.java:909)
at com.avail.easyloans.base.fragments.LocationFragment.sendLocationToClient(LocationFragment.java:192)
at com.avail.easyloans.base.fragments.LocationFragment.access$000(LocationFragment.java:46)
at com.avail.easyloans.base.fragments.LocationFragment$4.onSuccess(LocationFragment.java:220)
at com.avail.easyloans.base.fragments.LocationFragment$4.onSuccess(LocationFragment.java:214)
at com.google.android.gms.tasks.zzn.run(zzn.java:4)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6339)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1084)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:945)
What's wrong am I doing here?
I expect that each Activity is responsible for maintaining its own Fragments. This would means that if the Fragment you are trying to access is associated with another Activity's FragmentManager, then your app will fail.
I can think of a couple of ways around this, depending on the behaviour you want. If you don't mind having two LocationFragments having their own separate lifecycles in two separate activities, you can access them with findFragmentById() on your FragmentManager. I.e.:
private void openLocationFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
final Fragment current = fragmentManager.findFragmentById(R.id.fragmentContainer);
if(current == null || !(current instanceof LocationFragment)) {
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, LocationFragment.newInstance())
.commitAllowingStateLoss();
}
}
And do the same in your other Activity. Alternatively, you can invoke your other Activity that hosts the LocationFragment in onLocationFetched()
I have a single Activity with fragments and two simple methods inside the activity for managing fragments: pushFragmentImmediate() and popFragmentImmediate().
public void pushFragmentImmediate(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commitNow();
}
public void popFragmentImmediate() {
getSupportFragmentManager().popBackStackImmediate();
}
These transactions must execute immediately because of legacy code which depend on the back stack being up to date at all times.
Now I have a Fragment which pops off during onResume if some condition is satisfied:
#Override
public void onResume() {
super.onResume();
if (condition) {
((Main) getActivity()).popFragment();
}
}
This gives an error.
Can this error be avoided without changing popStackImmediate() to popBackStack()? Or can I move popBackStackImmediate() to some other phase when it will not return an error? Or am I thinking about this in the wrong way?
Full trace:
java.lang.RuntimeException: Unable to resume activity {se.esillen.testproject/se.esillen.testproject.Main}: java.lang.IllegalStateException: FragmentManager is already executing transactions
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3433)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3473)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2736)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1481)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:6152)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions
at android.support.v4.app.FragmentManagerImpl.ensureExecReady(FragmentManager.java:2167)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2223)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:834)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:794)
at se.esillen.testproject.Main.popFragment(Main.java:25)
at se.esillen.testproject.TestFragment.onResume(TestFragment.java:26)
at android.support.v4.app.Fragment.performResume(Fragment.java:2401)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1465)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1809)
at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3217)
at android.support.v4.app.FragmentManagerImpl.dispatchResume(FragmentManager.java:3181)
at android.support.v4.app.FragmentController.dispatchResume(FragmentController.java:214)
at android.support.v4.app.FragmentActivity.onResumeFragments(FragmentActivity.java:470)
at android.support.v4.app.FragmentActivity.onPostResume(FragmentActivity.java:459)
at android.support.v7.app.AppCompatActivity.onPostResume(AppCompatActivity.java:171)
at android.app.Activity.performResume(Activity.java:6856)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3410)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3473)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2736)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1481)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:6152)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
I have following situation
Let's take an example:
I have FirstActivity which contains Two fragments. Fragment-A is by default loaded to FirstActivity.By clicking on a button in Fragment-A screen moves to Fragment-B and it will add to back stack.From Fragment-B there is a button from where i visit to second activity. Now if any event occurs i am calling FragmentManager.popBackStack method to move Fragment-B to Fragment-A(This is application requirement) at that time my application get crashed. Because i am on SecondActivity and FragmentManager.popBackStack call from FirstActivity.
Now my code
When moving from Fragment-A to Fragment-B
MenuFragment menuFragment = MenuFragment.newInstance(orderObject.getOrderTableName(), orderObject.getTableSize(), arraySeatCount[k], arrayTableCode[k], arrayTableId[k], floorDetailObject.getId(), TabRabbitConstant.OrderType.DINEIN.orderType, "", "", false);
menuFragment.setTargetFragment(FloorFragment.this, UserDefault.ORDER_MENU);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameContainer, menuFragment, getResources().getString(R.string.M_MENU));
fragmentTransaction.addToBackStack(getResources().getString(R.string.M_MENU));
fragmentTransaction.commit();
When any event occur i am calling popbackstack on FirstActivity
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();
Exception i get when my application getting crash is
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860)
at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:770)
at com.app.tabrabbitpos.MainActivity.parseResponseInMain(MainActivity.java:691)
at com.app.tabrabbitpos.base.BaseActivity$3.run(BaseActivity.java:192)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Documentation of FragmentManager.popBackStack wrote that
This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.
So according to documentation i thought it should work but it gives me exception and my application get crashed.
I have already checked this link for my issue but not getting any solution
Is it safe to call FragmentManager.popBackStack when activity is paused
In short my question is
How to call FragmentManager.popBackStack when activity is paused? (Means when i am on second activity and FirstActivity is on Pause)
Create a boolean to check if activity is running for the first time like this
private Boolean mFirstRun=false;
Then in first activity onResume check if the activity is running for the first time and perform the action.
#Override
protected void onResume() {
super.onResume();
if(!mFirstRun){
mFirstRun=true;
}else {
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();
mFirstRun=false;
}
}
When the event is fired in second activity dont forget call finish() in second activity which will resume the first activity again.
getting IllegalStateException: Can not perform this action after onSaveInstanceState in activity when adding a fragment. It works most of the time but saw there are some time this IllegalStateException happening.
the add fragment is done after checking the activity is still there by:
if ((!mInSaveInstanceState) && !isFinishing())
any explanation why the "IllegalStateException: Can not perform this action after onSaveInstanceState" could still happen?
code snippet is blow.
in activity:
#Override
protected void onSaveInstanceState(Bundle outState) {
mInSaveInstanceState = true;
… …
super.onSaveInstanceState(outState);
}
snippet displaying a new fragment:
void addNewFragment () {
if ((!mInSaveInstanceState) && !isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
String fragmentName = "NEW_ADDED_FRAGMENT";
int holderId = R.id.new_fragment_holder;
NewFragment theNewFrgmt = new NewFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(holderId, theNewFrgmt, fragmentName);
ft.addToBackStack(fragmentName);
ft.setCustomAnimations(R.anim.hold, 0, R.anim.slide_left_in, 0);
ft.show(theNewFrgmt);
ft.commit(); //<=== it throws at here
}
}
the crash
Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1832)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1850)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:643)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:603)
at com.zone.ui.MainActivity.addNewFragment(MainActivity.java:4522)
at com.zone.ui.MainActivity.access$000(MainActivity.java:262)
at com.zone.ui.MainActivity$8.onEvent(MainActivity.java:591)
at com.zone.ui.MainActivity$8.onEvent(MainActivity.java:587)
at com.zone.dataModel.EventManager.dispatchEvent(EventManager.java:96)
at com.zone.ui.tasklist.TaskListFragment$8.run(TaskListFragment.java:422)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5343)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
After onSaveInstanceState you can't commit transaction, so use StateLoss commit.
ft.commitAllowingStateLoss();