Why am I getting IllegalStateException: FragmentManager is already executing transactions - android

There are a several questions addressing that error but none of them have had a fix that worked for this situation.
Whats happening is that we are popping a fragment (which is nested inside a featureFragment) and making a call back to the activity to remove the feature fragment and un-hide a different fragment containing a googleMap.
DetailFragment:
if (mConversationId != null) {
try {
return getLayerClient().getConversation(mConversationId);
} catch (LayerException exc) {
getFragmentManager().popBackStack();
((MainActivity) getActivity()).LayerConnectionLost();
return null;
}
Main Activity:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Only hide the map fragment in order to keep the Google Map in memory as long as possible to avoid being charged
if (fragment != mMapFragment && !mMapFragment.isHidden()) {
transaction.hide(mMapFragment);
}
// Detach the other fragment views
if (fragment != mToolsFragment && !mToolsFragment.isDetached()) {
transaction.detach(mToolsFragment);
}
if (mMessageFragment != null ) {
if (fragment != mMessageFragment && !mMessageFragment.isDetached()) {
transaction.detach(mMessageFragment);
}
}
mVisibleFragment = fragment;
if (fragment == mMapFragment) { // map fragment is hidden, so show
transaction.show(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
} else { // other fragments are detached, so attach
transaction.attach(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
}
getSupportFragmentManager().executePendingTransactions();
The Stack Trace is giving the error on the last line
getSupportFragmentManager().executePendingTransactions();
But in testing I found that it is actually this line of code which is precipitating the error.
transaction.show(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
Any ideas what I'm dong wrong?

Related

UI Blocked when a global exception is caught

In my app I'm trying to handle global exceptions using Thread.UncaughtExceptionHandler.
When my app finds any uncaught exception, I'm trying to catch it globally in the Application class and also in fragments.
My application has many fragments on an activity. If it crashes in any of the fragment I want the app to remove that fragment and it should show the previous fragment and it is working as desired. But the UI(main thread) is blocked. If I try to access anything in the main thread, it shows a dialog saying 'app is not responding'
#Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(TAG + "The Uncaught exception is-----------------------------------------------------------------------------");
e.printStackTrace();
if (activityContext != null){
removeTopfragment(); //removes the top most fragment
}
}
private void removeTopfragment() {
System.out.println("inside removeTopfragment");
FragmentManager manger = activityContext.getSupportFragmentManager();
FragmentTransaction ft = manger.beginTransaction();
List<Fragment> list = manger.getFragments();
//get last one
if(list.size() > 0) {
System.out.println("inside list.size() ");
Fragment topFragment = list.get(list.size() - 1);
ft.remove(topFragment);
ft.commitAllowingStateLoss();
}
}
I don't want to kill/restart the app. Please help

Load fragment on InfoWindowClick

So I'm trying to do the following in android xamarin.
When you press on a map element an infowindow is shown.When you press that window and an event is linked to the element the app goes to another fragment that is described by the action object. The problem is the moment I press the infowindow the whole app freezes and nothing happens. In the logs I can't see anything and the app stops at the following line:
pager.Adapter = pagerAdapter;
Added a breakpoint there and after saying "step over" the ide doesn't break anymore and the app freezes (no user interaction possible).
So let me start by giving all the relative code and a little explanation.
So first I'll show you what happens on the infowindow click. This happens on a SupportMapFragment that has it's own listener.
void GoogleMap.IOnInfoWindowClickListener.OnInfoWindowClick (Marker p0)
{
InfoPopup ip = CustomJsonConverter.Convert<InfoPopup> (p0.Title);
if (ip == null || ip.Goto == null || !(this.Activity is MainView))
return;
MainView main = (this.Activity as MainView);
p0.HideInfoWindow ();
switch (ip.Goto.type) {
case "InfoFragment":
Info info = InfoController.Items.Find (x => x.Index == ip.Goto.id);
if (info != null) {
main.RunOnUiThread (() => {
main.ShowInfosFragment ();
main.ShowInfoFragment (info);
});
}
break;
case "FaqFragment":
FaQ faq = FaQController.Items.Find (x => x.Index == ip.Goto.id);
if (faq != null) {
main.RunOnUiThread (() => {
main.ShowFaqsFragment ();
main.ShowFaqFragment (faq);
});
}
break;
}
}
I tested it with an action of InfoFragment which gives back an item so that's good. Then it goes to the main.ShowInfosFragment() which is where it freezes. So that method is really simple. This function is in the Activity holding the fragments (thanks Cheesebaron).
public void ShowInfosFragment(){
ShowFragment(1, new InfosFragment (){ InfoSelectedAction = ShowInfoFragment });
}
So the problem giving function is the following. This function is in the Activity holding the fragments (thanks Cheesebaron).
protected void ShowFragment(int index, Android.Support.V4.App.Fragment fragment, bool ignoreType = false){
RemoveSubMenu();
if (pagerAdapter.Count <= index || ignoreType || !(pagerAdapter.GetItem (index).GetType().Equals(fragment.GetType()))) {
pagerAdapter.AddFragment (index, fragment);
SetSubMenu (fragment);
pager.Adapter = pagerAdapter;
}
pager.SetCurrentItem (index, true);
DrawerButtonLayout ();
}
I've used this function a lot and it has always worked to move to the other fragments from the menu or at startup to set the mapfragment.
Anyone sees what's the problem with my code? Tried tons of things but can't seem to figure this one out with my friend google.
Thanks already for reading.
Kind regards,
Karl

FragmentTransaction crashes after checking if (Fragment != null)

I was following this tutorial on youtube and ran into a problem with the end result.
The goal of the tutorial was to introduce Fragment Transactions by having these buttons that add/remove/replace fragments in a layout below the buttons. It all went smoothly until the end.
If I hit the "Add A" button, it adds the Frag_A in the layout below, if you hit remove, it disappears. However, if you hit "Add A" 2+ times, then you need to hit remove 2+ times in order to get rid of all of them. Likewise, if you hit "Add A" and then "Add B", you need to hit "Remove A" first before you can see fragment B.
At the end of the video, they included an if statement in the "Remove" methods to check whether or not the Fragment was already there.
public void RemoveA (View v) {
FragmentA FA = (FragmentA) manager.findFragmentByTag("A");
FragmentTransaction transaction = manager.beginTransaction();
if (FA != null) {
//remove the transaction and commit
}
else {
//Toast a message to say the FragmentA doesn't exist yet
}
}
So I thought I could add a similar thing into the "AddA" method to check if a FragmentA exists, then toast a message to say it's already there, and if it doesn't exist, to add it to avoid having so many of them get created when you only need 1.
public void AddA (View v) {
FragmentA FA = (FragmentA) manager.findFragmentByTag("A");
FragmentTransaction transaction = manager.beginTransaction();
if (FA != null) {
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show());
}
else {
transaction.add(FA);
transaction.commit();
}
}
However, this made it so when you hit "AddA", the program just gives an unexpected error and quits. Looking at the Logcat (I just started learning last week so I don't know what everything means), I noticed a line mentioning a nullpointerexception at:
transaction.add(FA);
Meanwhile, IntelliJ is saying that:
if (FA != null)
Is always true, and FA == is always false. I also tried appending && isvisible but that didn't make a difference either. Is there any ideas as to why this would happen?
This is my first post and I couldn't find an answer for this on google/searching.
Looking at your code:
if (FA != null) { // here FA is not null
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show()); }
else { // here, FA is null!
transaction.add(FA); // same as transaction.add(null);
transaction.commit();
}
In the else block, you're missing some code where you would create a new fragment in FA, before adding it to the transaction.
if (FA != null) {
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show()); }
else {
// Create a new fragment here and set it to FA
transaction.add(FA);
transaction.commit();
}

Fragments android orientation loss of fragment

I'm having a struggle with fragment loss on orientation change.
At first my fragments seem to be responding well to orientation changes and nothing is wrong.
But the exception is when I navigate back to a previously made fragment. Since I'm checking that in my fragmenthandler activity.
I chose to use a big if statement instead of switch cases, since that was more within the grasp of my knowledge. The handling of the if statements works correctly, have been debugging it with log statements and nothing seems to be wrong there. Below is the code for the handling of fragments, pre-used or create a new one.
public void fragmentHandle(Fragment fl, Fragment fr, String tl, String tr) {
Fragment cFragLeft = manager.findFragmentById(R.id.fragment_left);
Fragment cFragRight = manager.findFragmentById(R.id.fragment_right);
Fragment checkLeft = manager.findFragmentByTag(tl);
Fragment checkRight = manager.findFragmentByTag(tr);
FragmentTransaction ft = manager.beginTransaction();
if (cFragLeft != null && cFragRight != null && checkLeft != null
&& checkRight != null) {
ft.detach(cFragRight).detach(cFragLeft).attach(fr).attach(fl)
.addToBackStack(null).commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight == null) {
ft.detach(cFragLeft).detach(cFragRight)
.add(R.id.fragment_left, fl, tl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fr)
.add(R.id.fragment_left, fl, tl).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null
&& checkRight == null && checkLeft != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else {
ft.add(R.id.fragment_left, fl, tl).add(R.id.fragment_right, fr, tr)
.commit();
}
}
When I change orientation, and then navigating back to a previously saved fragment (first if statement) both of my left and right fragments show blank screens. This happens only when case 1 is called, it doesn't seem to remember the fragments that were there, even though the fragmentmanager found them by tag.
I've also been checking my onCreate, and it's shown below.
protected void onCreate(Bundle savedInstanceState) {
// super.onCreate
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_handler);
if (savedInstanceState == null) {
fragmentHandle(nFragLeft, nFragRight, "menuMain", "main");
}
}
Also including a button statement just for completion's sake. Haven't been using onClickListeners and not really sure whether I should prefer this over xml onClick or not.
public void sendDestination(View v) {
nFragLeft = menudestFrag;
nFragRight = destFrag;
String tagLeft = "menuDestination";
String tagRight = "destination";
fragmentHandle(nFragLeft, nFragRight, tagLeft, tagRight);
}
Is there any way to work around this? Only happens after changing orientation.
This happens because your activity is reacreated when orientation is changed.
you can solve this problem adding android:configChanges="orientation|screenSize" in manifest(prevent to recreate activity when orientation was changed):
<activity
android:name="com.example.MyActivity"
android:configChanges="orientation|screenSize" >
</activity>

Fragment.isAdded returns true after fragment removed from a container

I have an activity with below layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/frameLayoutA"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1" >
</FrameLayout>
<FrameLayout
android:id="#+id/frameLayoutB"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1" >
</FrameLayout>
</LinearLayout>
In onCreate of activity, I load Fragment_A in frameLayoutA and Fragment_B in frameLayoutB.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
fmA=(FrameLayout) findViewById(R.id.frameLayoutA);
fmB=(FrameLayout) findViewById(R.id.frameLayoutB);
fragA=new FragmentA();
fragB=new FragmentB();
fragC=new FragmentC();
addFragmentsInActivity(R.id.frameLayoutA,fragA);
addFragmentsInActivity(R.id.frameLayoutB,fragB);
}
public void addFragmentsInActivity(int id, Fragment fragment)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(id, fragment);
fragmentTransaction.commit();
}
Using a menu operation I want to load Fragment_B in frameLayoutA and Fragment_C in frameLayoutB. The Menu operation is:
removeFragmentsInActivity(R.id.frameLayoutB,fragB);
addFragmentsInActivity(R.id.frameLayoutB,fragC);
if(!fragB.isAdded()){
Log.e("check", "fragB already removed from frameLayoutB");
removeFragmentsInActivity(R.id.frameLayoutB,fragB);
addFragmentsInActivity(R.id.frameLayoutA,fragB);
}
else{
Log.e("check", "fragB already added");
}
public void removeFragmentsInActivity(int id, Fragment fragment)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
}
Fragment_B is not displayed in frameLayoutA. frameLayoutA shows Fragment_A. When Menu operation is clicked again the Fragment_B is loaded.
Debugging I found that after fragB.isAdded() returns true after fragB.remove() operation is done. During 2nd menu operation fragB.isAdded() return false and fragB.add() is executed and FragmentB is shown in frameLayoutA.
I understand commit is an async operation. isAdded return true because commit is async and remove operation commit is not done till the time fragB.isAdded() is called. Is it true?
Kindly suggest the possible solution or approach to solve the issue.
Regards,
Vibhor
Yes the transaction is committed asynchronously. If you want to make sure all trasactions have finished before executing isAdded, run:
getFragmentManager().executePendingTransactions();
From documentation for executePendingTransactions():
After a FragmentTransaction is committed with
FragmentTransaction.commit(), it is scheduled to be executed
asynchronously on the process's main thread. If you want to
immediately executing any such pending operations, you can call this
function (only from the main thread) to do so. Note that all callbacks
and other related behavior will be done from within this call, so be
careful about where this is called from.
So your code should look like:
removeFragmentsInActivity(R.id.frameLayoutB,fragB);
addFragmentsInActivity(R.id.frameLayoutB,fragC);
getFragmentManager().executePendingTransactions();
if(!fragB.isAdded()){
Log.e("check", "fragB already removed from frameLayoutB");
removeFragmentsInActivity(R.id.frameLayoutA,fragA);
addFragmentsInActivity(R.id.frameLayoutA,fragB);
}
else{
Log.e("check", "fragB already added");
}
Note also fixed removing of fragment A.
maybe your can encapture commition of FragmentTransaction like this
private void commitFragmentTransaction(final FragmentTransaction ft, boolean allowStateLoss, boolean now) {
if (ft == null || ft.isEmpty()) {
return;
}
if (allowStateLoss) {
if (now) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ft.commitNowAllowingStateLoss();
} else {
ft.commitAllowingStateLoss();
mFragmentManager.executePendingTransactions();
}
} else {
ft.commitAllowingStateLoss();
}
} else {
if (now) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ft.commitNow();
} else {
ft.commit();
mFragmentManager.executePendingTransactions();
}
} else {
ft.commit();
}
}
}
commitNow() and commitNowAllowingStateLoss() is Added in API level 24
Calling commitNow is preferable to calling commit() followed by executePendingTransactions() as the latter will have the side effect of attempting to commit all currently pending transactions whether that is the desired behavior or not.

Categories

Resources