How to prevent FragmentManager from being destroyed after rotation - android

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.

Related

Avoid IllegalStateException when doing popBackStackImmediate during onResume in fragment

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)
 

How to call FragmentManager.popBackStack when activity is paused?

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.

Android - Fragment Transition issue

I currently am working on a newsfeed module where i have this issue of fragment transition.
Transition Example :
Newsfeed >a> Profile >b> Detail >c> Profile.
Newsfeed >a> Detail >b> Profile >c> Detail.
Every time i visit a page twice and tries to back again at "a". My app crashed giving nullpointerexception on
getActivity().onBackPressed();
in fragment. Each stack is poped in MainActivity in override onBackPressed. All flows are fine when there are no 2 of the same fragment transition in the backstack.
PopBackStack Example :
getSupportFragmentManager().popBackStack("profile", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Let me know if you need more codes to help.
Any help would be appreciated.
Thank you!
Edit:
Logcat Error :
09-19 02:15:02.399 29778-29778/com.dbs.alive E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.dbs.alive, PID: 29778
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.FragmentActivity.onBackPressed()' on a null object reference
at com.dbs.alive.ProfileFragment$1.onClick(ProfileFragment.java:171)
at android.view.View.performClick(View.java:5210)
at android.view.View$PerformClick.run(View.java:21328)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5551)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
Main Activity (onBackPressed)
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
//this.finish();
super.onBackPressed();
} else {
for (int i = getSupportFragmentManager().getBackStackEntryCount(); i > 0; i--) {
String ide = getSupportFragmentManager().getBackStackEntryAt(i - 1).getName();
if (ide != null) {
//1. FROM NEWSFEED
if (ide.equals("newsfeed")) {
getSupportFragmentManager().popBackStack("newsfeed", FragmentManager.POP_BACK_STACK_INCLUSIVE);
break;
} else if (ide.equals("profile")) {
getSupportFragmentManager().popBackStack("profile", FragmentManager.POP_BACK_STACK_INCLUSIVE);
break;
}
Fragment AddBackStack
Fragment fragment = ProfileFragment.newInstance("", "");
Bundle mBundle = new Bundle();
mBundle.putString("profileid", postOwnerId);
mBundle.putString("profilename", postOwnerName);
mBundle.putString("profileicon", RealmHelper.getUserProfileUrl(postOwnerId));
fragment.setArguments(mBundle);
FragmentTransaction transaction = ((AppCompatActivity) activity).getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.newsfeed_layout, fragment);
transaction.addToBackStack("profile");
transaction.commit();

Can not perform this action after onSaveInstanceState on setMap

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.

what could be the reason even if check isFinishing() it still gets IllegalStateException: Can not perform this action after onSaveInstanceState

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

Categories

Resources