How to call FragmentManager.popBackStack when activity is paused? - android

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.

Related

Caused by java.lang.IllegalStateException Can not perform this action after onSaveInstanceState

I have an activity and a fragment within that activity. The fragment is loaded within the activity onCreate().
if (!supportFragmentManager.isDestroyed) {
val fragmentTransaction = this.supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.containerLayout, fragment).commit()
}
Inside the fragment, I am performing an API call and when the result is received, the activity gets the callback and the result is passed to the fragment from the activity.
The issue is when I load this activity and when the API is still on call if I press the device recents button then the app crashes showing the below exception.
Caused by java.lang.IllegalStateException Can not perform this action after onSaveInstanceState
I understand that the problem is the fragment tries to commit after onSaveInstanceState is called. But how is that happening I am not clear. I went through the article too. It says three points as solution.
To commit the fragment within onCreate() which I am already doing.
Not to commit in onPostExecute() which is not applicable to me.
Use commitAllowingStateLoss() only as a last resort.
Should I need to change commit() to commitAllowingStateLoss()? As I went through the docs, I don't feel that safe too. Could someone suggest to me the right way?
I didn't use commitAllowingStateLoss(). I put the code as:
var isAnException: Boolean = false
try {
if (!supportFragmentManager.isDestroyed) {
val fragmentTransaction = this.supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.containerLayout, fragment).commit()
}
} catch (exception: IllegalStateException) {
isAnException = true
}
and in onResume() of my activity, I added the below code to make the fragment work when taken from the recents.
override fun onResume() {
if (isAnException) {
isAnException = false
//fragment load and set the views
}
super.onResume()
}

IllegalStateException while showing DialogFragment after back from AppCompatActivity

I am starting a FragmentActivity from a custom DialogFragment and after I come back the original Activity and try to open the dialog again I get:
IllegalStateException: Can not perform this action after onSaveInstanceState
I don't understand why is it happening, if I dismiss the dialog from the original activity than I can show it again as many times as I want, but if I start a new Activity from the DialogFragment after that I cannot show any dialog again because I get the exception.
Here is my code, this method is in my DialogFragment:
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3){
mSearchLocationType = position;
switch(position){
case PSL_CURRENT_POSITION: // I can show the dialog again after this.
break;
case PSL_MAP_POINT:
Intent selectMapPoint = new Intent(parentActivity, SelectMapPointActivity.class);
selectMapPoint.putExtra(SelectMapPointFragment.EXTRA_SELECTED_POS, mSearchLocation);
parentActivity.startActivityForResult(selectMapPoint, REQ_MAP_POINT);
// After returning this Activity and trying to show ANY DialogFragment the app crashes.
break;
case PSL_ADDRESS:
Intent selectAddress = new Intent(parentActivity, SelectAddressActivity.class);
parentActivity.startActivity(selectAddress); // Also from this Activity, makes no difference.
break;
}
dismiss();
}
I am showing the dialog like:
if(!selectPoiAroundDialog.isVisible())
selectPoiAroundDialog.show(parentActivity.getSupportFragmentManager(), "mSelectPoiCategoryDialog");
What I have tried already:
Override the show() method of the DialogFragment and use this code to show the dialog:
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
Debug the app and check if all the lifecycle methods are called, but it seems normal to me, each of the times the onDestroyView() method of the DialogFragment is called.
Override the show() and onDismiss() method of the DialogFragment and track if it is shown or not.
This is a corporate app and has a bad design, so the class which I am trying to modify (where I am showing the dialog) is not child of Activity or Fragment. ´parentActivity´ is accessed by a static method of the application class, but I am sure that it has the right Activity. Do you think this can cause the problem?
EDIT:
The full stacktrace is when I useing commitAllowStateLoss():
java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1515)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:638)
at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:621)
at com.mycompany.dialog.GTDialog.show(GTDialog.java:174)
at com.mycompany.fragment.content.SearchOfflinePOIContent$2.onClick(SearchOfflinePOIContent.java:250)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21209)
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:5525)
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)
I also tried to override the onSaveInstanceState() method, it didn't helped.
EDIT2:
The original stacktrace is:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1493)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1511)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:638)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:617)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:139)
at com.mycompany.dialog.GTDialog.show(GTDialog.java:174)
at com.mycompany.fragment.content.SearchOfflinePOIContent$1.onClick(SearchOfflinePOIContent.java:197)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21209)
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:5525)
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)
If I override the show() method and try to commit the fragment with commitAllowingStateLoss() than I get the first exception.
I figured out the problem. When the app quit from an Activity it didn't remove the reference of it from parentActivity, so when I tried to show the dialog again I gave a reference of the destroyed activity.
Thanks for your time!

How to save fragment state while navigating through activity

May be the title of the question is not clear . Or I have failed to describe what I am looking for .
I have two activity in my app . Lets say their name is "ActivityA" and "ActivityB" . Under "ActivityA" there are few fragments . Lets say their name is "FragmentA" "FragmentB" "FragmentC" "FragmentD" etc . The default fragment is "FragmentA" , that means when the activity starts "FragmentA" starts .
Now , if I navigate to "ActivityB" , and came back to "ActivityA" , it always open "FragmentA" . But what I want is if I navigate to "FragmentB" , and then navigate to "ActivityB" , and back to "ActivityA" it starts "FragmentA" , but I want "FragmentB" to start ,in which I was beforer
You might be restarting ActivityA from ActivityB via startActivity() instead of finishing ActivityB. If it is so, just finish your ActivityB. Then you will be on the last shown fragment in your ActivityA.
Or you can also use onSaveInstanceState method to keep trace of your Fragments. This answer can help you with this.
Or using following with your ActivityA can also solves your issue:
<activity android:name=".ActivityA" android:launchMode="singleTop">
Not sure if I understand you correctly, but I think what you need to do is startActivityForResult when you call your ActivityB. When you press BackButton you provide your necessary information about the fragment to be called and catch these infos in ActivityB's onActivityResult then navigate to the desired Fragment.
Create a method that returns the last visible fragment
public Fragment getVisibleFragment(){
FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null){
for(Fragment fragment : fragments){
if(fragment != null && fragment.isVisible())
return fragment;
}
}
return null;
}
Create an instance of Fragment class and set it to null
Fragment lastVisibleFragment = null;
in onPause save the last fragment
#Override
protected void onPause() {
super.onPause();
lastVisibleFragment = getVisibleFragment();
}
Then in onResume() add the last fragment you were in if not null
#Override
protected void onResume() {
super.onResume();
if(lastVisibleFragment !=null){
getSupportFragmentManager().beginTransaction()
.add(yourFrameLayout, lastVisibleFragment)
.commit();
}
else{ //Add first fragment you were adding}
}

When Don't Keep Activities option on, onSavedInstanceState is giving null in Activity A

Flow Image I am facing a strange issue related to saving the state in Android.
When I turn on Don't Keep Activities option in Android(From Developer Option). Then if I launch Activity B(which launches Fragment B) from Fragment A (launched by Activity A). Then my onSavedInstance method is getting called in Fragment A but in OnCreate method of Fragment A, I am getting received bundle as null.
And above is inconsistent means sometimes (Specially reproduced when clear the data Android).
In my Activity A in method onSaveInstanceState I have saved my Fragment A with below impl -:
getSupportFragmentManager().putFragment(Bundle,FRAGMENT_TAG,instanceOFFragA);
Also in my onCreate I am trying to retrieve the saved Fragment A with below impl -:
if (savedInstanceState != null) {
args = savedInstanceState;
Fragment fragment = getSupportFragmentManager().getFragment(savedInstanceState, FRAGMENT_TAG);
if (fragment != null && fragment.getClass().equals(FragA.class)) {
instanceOfFragA = (FragA) getSupportFragmentManager().getFragment(savedInstanceState, FRAGMENT_TAG);
}
}
But in OnCreate I am getting instance of Fragment C (which is basically a Registration Fragment which have Login & Register button, Login click will launch LoginActivity which inturn launches LoginFragment. If login get success it calls back on Activity A which launches Fragment A(as login is success)).

Back event is sent to previous activity

I have 2 Activities as below:
public class MainActivity extends Activity {
....
profile.login(new onCallback()) {
#Override
public void onResult(int result) {
final Intent i = new Intent(MainActivity.this, DetailActivity.class);
startActivity(i);
}
}
....
}
public class DetailActivity extends Activity {
....
}
Where profile.login run on a AsyncTask and callback when there is a result.
Once result received, it will start the 2nd activity.
Here is the trouble steps:
On MainActivity, press on the login button.
Press BACK to close the App.
The app is closed but after a while the 2nd activity popup and immediately follow by a force close.
I trace the life cycle event, logcat shows:
MainActivity.onClickLogin
DetailActivity.onCreate started.
DetailActivity.onCreate ended.
MainActivity.onDestroy started.
MainActivity.onDestroy ended.
It is very time-dependent and only happens between "DetailActivity.onCreate ended" and the screen is not shown. Once the DetailActivity screen shown, press Back will not cause MainActivity to destroy. It just destroy the DetailActivity as expected:
MainActivity.onClickLogin
DetailActivity.onCreate started.
DetailActivity.onCreate ended.
DetailActivity.onDestroy started.
DetailActivity.onDestroy ended.
My Question is:
Since DetailActivity is already created (onCreated ended) should the back key event sent to DetailActivity and cause it destroy? Why the key is sent to MainActivity?
Any idea how to counter-measure this?
The Android version I am working on is 4.0.x
Create header and footer. set a one image and write a code on onclick listener...

Categories

Resources