Avoid IllegalStateException when doing popBackStackImmediate during onResume in fragment - android

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)
 

Related

How to prevent FragmentManager from being destroyed after rotation

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.

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

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

getContext() is NULL for a certain fragment flow

2 Fragments & 1 Activity are involved. Here is the flow wherein I am able to reproduce the issue, along with relevant code snippets.
#1. From Frag1, open Frag2
Frag2 fragment = Frag2.newInstance(pos);
BaseFragment.addToBackStack(getContext(), fragment);
Here is BaseFragment.addToBackStack()
BaseFragment
public static void addToBackStack(Context context, BaseFragment fragment) {
FragmentManager fragmentManager = ((BaseActivity) context).getSupportFragmentManager();
android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(BaseActivity.getContainerIdForCurrentActivity(context), fragment);
transaction.addToBackStack(null).commit();
}
#2. Press Back to open Frag1 again.
#3. Open Activity A1 with startActivityForResult() from an Adapter in Frag1.
Intent intent = new Intent(context, Act1.class);
intent.putExtra(..);
intent.putExtra(..);
((BaseActivity) context).startActivityForResult(intent, Frag1.REQ_CODE_ISSUE_DONE);
#4. Go back to Frag1, either by pressing Back or finishing A1 with
setResult()
#5. onActivityResult() of Frag1 is called. But inside it, both getContext() and getActivity() are NULL.
Frag1
if (resultCode == Ac1.RESULT_CODE_OPEN_LANDING_SCREEN) {
if (getActivity() instanceof MainActivity) {
BaseFragment.replaceStack(getContext(), landingFrag.newInstance());
} else {
Intent intent1 = new Intent(getContext(), MainActivity.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent1);
}
}
If A1 is opened directly from Frag1, then getContext() is NOT null inside startActivityForResult() and works perfectly.
Logcat:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=792, result=135, data=null} to activity {in.shadowfax.gandalf/in.shadowfax.gandalf.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ComponentName.<init>(ComponentName.java:128)
at android.content.Intent.<init>(Intent.java:4449)
at in.shadowfax.gandalf.help.issues.IssuesFrag.onActivityResult(IssuesFrag.java:153)
at in.shadowfax.gandalf.help.HelpPresenter.triggerIssuesFragOnActivityResult(HelpPresenter.java:72)
at in.shadowfax.gandalf.help.HelpFrag.onActivityResult(HelpFrag.java:100)
at in.shadowfax.gandalf.MainActivity.onActivityResult(MainActivity.java:901)
at android.app.Activity.dispatchActivityResult(Activity.java:6428)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
at android.app.ActivityThread.-wrap16(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:148) 
at android.app.ActivityThread.main(ActivityThread.java:5417) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
Instead of
((BaseActivity) context).startActivityForResult(intent, Frag1.REQ_CODE_ISSUE_DONE);
using
fragment.startActivityForResult(intent, Frag1.REQ_CODE_ISSUE_DONE);
solved the issue.
onActivityResult() inside Nested Fragments is now called in support lib 23.2. So no need to reroute the onActivityResult() from Actvity to frag manually as I was doing in my case.
Ref: https://inthecheesefactory.com/blog/onactivityresult-nested-fragment-support-library-v23.2/en

Android findFragmentById returns null

I have this problem when working on communicating from the main activity to fragment. The Monitor keeps saying:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.mick.myApplication.FragmentCal.readfiles(java.util.LinkedList)' on a null object reference
at com.example.mick.myApplication.main.onCreate(main.java:101)
at android.app.Activity.performCreate(Activity.java:6289)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758) 
at android.app.ActivityThread.access$900(ActivityThread.java:177) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:145) 
at android.app.ActivityThread.main(ActivityThread.java:5942) 
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:1399) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) 
This is the code for fragment transaction and sending the message:
fragmentinfo = new FragmentInfo();
fragmentcal = new FragmentCal();
fragmentrep = new FragmentRep();
fragmentdicn = new FragmentDicn();
fragmentManager = getSupportFragmentManager();
if (!fragmentcal.isAdded()) {
if (currentFragment == null) {
fragmentManager.beginTransaction().add(R.id.fragmentlayout, fragmentcal).commit();
} else {
fragmentManager.beginTransaction().hide(currentFragment).add(R.id.fragmentlayout, fragmentcal).commit();
}
FragmentCal fragmentCal = (FragmentCal) getSupportFragmentManager().findFragmentById(R.id.fragmentlayout);
fragmentCal.readfiles(nameList);
} else {
fragmentManager.beginTransaction().hide(currentFragment).show(fragmentcal).commit();
}
currentFragment = fragmentcal;
And for that, I import android.support.v4.app.Fragment for all activities and fragments.
You can't perform a findFragmentById before adding the fragment to your activity. When you make a setContentView the Activity inflates the layout and adds its views to the current window. In the case of having a Fragment in this layout, it is added with the given Id or Tag provided in the xml to the FragmentManager. Only in this moment you can find it there. So what you have to do is move the findFragmentById below the setContentView.
Source: NullpointerException when findFragmentById
Hope it helps! Good luck!

Categories

Resources