Call fragment from activity in android - android

I am current working on project which is in used Fragment. But Here, When i call to activity class From Fragment it's run perfectly. What i have to do is that on Back Pressed i need to call a Fragment.But i can't , it shows me error and my app stops.
So my question here is that how can i call fragment from activity so that my sequence should be fragment>activity>fragment.
07-11 16:22:12.190: E/AndroidRuntime(11963): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
& when i want to call activity from fragment it's give error
07-11 15:52:25.961: E/FragmentManager(11885): No view found for id 0x7f05003c for fragment
So, how can i call fragment from activity & activity from fragment?

Try changing
transaction.commit();
to
transaction.commitAllowingStateLoss();
Or comment out the super onSaveInstance method on your activity:
#Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
}
To call activity from a fragment you can use:
((YourActivity)getActivity).someMethod();

java.lang.IllegalStateException:
Can not perform this action after onSaveInstanceState
Solution:
Use transaction.commitAllowingStateLoss(); when adding or performing
the FragmentTransaction that was causing the Exception.
Why was the exception thrown?
The exception was thrown because you attempted to commit a FragmentTransaction after the activity's state had been saved, resulting in a phenomenon known as Activity state loss.
When you call FragmentTransaction#commit() after onSaveInstanceState() is called, the transaction won't be remembered because it was never recorded as part of the Activity's state in the first place. From the user's point of view, the transaction will appear to be lost, resulting in accidental UI state loss. In order to protect the user experience, Android avoids state loss at all costs, and simply throws an IllegalStateException whenever it occurs.
NOTE:
Use commitAllowingStateLoss() only as a last resort. The only difference between calling commit() and commitAllowingStateLoss() is that the latter will not throw an exception if state loss occurs. Usually you don't want to use this method because it implies that there is a possibility that state loss could happen. The better solution, of course, is to write your application so that commit() is guaranteed to be called before the activity's state has been saved, as this will result in a better user experience. Unless the possibility of state loss can't be avoided, commitAllowingStateLoss() should not be used.
More from: fragment-transaction-commit-state-loss.
FragmentManager(11885): No view found for id 0x7f05003c for fragment
Solution:
The Fragment manager cannot able to find a view with R.id.Container according to what you set in a layout at setContentView of activity.
So whatever layout you have set in setContentView, that layout doesn't contain that view with that id that resolves to id 0x7f05003c say of R.id.Container.

Related

When will a dynamically added fragment be re-added?

The Fragment documentation shows an example of an activity dynamically adding a fragment in onCreate(...):
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
This almost makes sense to me, except for one detail. I thought the reason for checking savedInstanceState == null was that if the activity is being re-created, we can expect the framework to re-add the fragment for us. However, I thought the framework would only do this if the fragment has a tag, and the example uses the version of FragmentTransaction#add(...) that does not take a tag. So as I understand it, if this activity were recreated, it would not have a DetailsFragment.
Is my understanding wrong? And if the framework does re-add the fragment, at what point in the activity's lifecycle is it guaranteed to have done so?
I thought the framework would only do this [re-add the fragment] if the fragment has a tag
No. According to the documentation in "Adding a fragment to an activity" section:
each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted.
Thus, you have 3 possibilities to handle this: using an unique id, unique tag or none of them. Yes, neither of the previous two requirements.The purpose of adding a tag is to capture the fragment. This is useful if you want to retrieve it easily and play with it (such as performing transactions). However, it's not required.
When you use add(int, Fragment), it calls add(int, Fragment, String) with a null tag at the 3rd parameter. Therefore, the system will use the ID of the container view instead (1st param in add()). Thus, the fragment is restored without any id or tag that you supplied but correctly handled by the system.
Note: in reference of add(int, Fragment, String), you can see this quote: "Optional tag name for the fragment, to later retrieve the fragment with FragmentManager.findFragmentByTag(String)" - How could it be optional if we need it to restore fragments? ;)
At what point in the activity's lifecycle is it guaranteed to have done so?
I cannot honestly respond on it, maybe someone has the right answer but, here's what I think.
As you can see in "Handling configuration changes", when orientation (or any specific change) occurs on activity, it calls onDestroy() and directly onCreate(). At the point of (activity's) onCreate() is called, the child fragment will receive a callback in onAttach(). Beside, when the activity has received its onCreate() callback, a child fragment receives automatically a onActivityCreated() callback. After that, each method in activity will trigger the "exact" same child methods (onStart(), onResume(), onPause(), onStop()).
Thus, I think the safe point is onStart(), because activity triggers directly the call of the fragment's method, you're sure that the fragment is attached to it and you can handle onRestart() -> onStart() to update the UI.
I don't use tags with my Fragments and mine work just fine, I have the exact same test as you.
if (savedInstanceState == null) {
//fragment needs to be created
}
if (savedInstanceState != null) {
//fragment will automatically be there with no code
}

Memory leak in FragmentManager

Update:
I think the leak is coming from getActivity().getSupportLoaderManager().restartLoader(getLoaderId(), null, this);
where i have my object implement LoaderCallback. Is there a way for me to clear the callback i tired setting it to
getActivity().getSupportLoaderManager().restartLoader(getLoaderId(), null, null);
but this crashes
Orig:
I have a list of objects in one of my fragments(A). When I navigate forward I add fragment A to the backstack. After I have navigated to a new fragment and I dump the heap. I still see my object in the heap. When I get the shortest path in the dump it looks like below. I can see that in FragmentManagerImpl there is a reference to fragment A in mActive fragments which is keeping my lists object alive.
Is my fragment supposed to stay in mActive fragments or is this a leak?
Adding to backstack
FragmentTransaction transaction = mFragmentManager.beginTransaction();
updateTransactionWith(info.getReplacement(), transaction, "replace");
transaction.addToBackStack(info.getReplacement().getClass().toString());
transaction.commit();
mFragmentManager.executePendingTransactions();
By calling addToBackStack(), you're requesting the FragmentManager that the Fragment being replaced be just stopped and not destroyed because you're either anticipating that a back button press is very likely or, the Fragment is heavy on initialization and you would still like to avoid doing it again even though the user is not very likely to go back.
The docs clearly state that
If you do not call addToBackStack() when you perform a transaction
that removes a fragment, then that fragment is destroyed when the
transaction is committed and the user cannot navigate back to it.
Whereas, if you do call addToBackStack() when removing a fragment,
then the fragment is stopped and will be resumed if the user navigates
back.
Hence, it's not a memory leak and your observations are quite in line with the expected behaviour.
However, just like an Activity, the system may still choose to destroy this Fragment, if it's running out of memory. But, that's expected behaviour too.
It's not a memory leak. You need to decide how to deal with your fragment's state.
Ideally you implement onSaveInstanceState and onViewStateRestored saving your state to the bundle and restoring it from the bundle respectively.
Alternatively, if you're able to re-create your state easily, you may want to save the bother of (re)storing it using the bundle and just null your references in the onPause method and create them during the onResume method. Be aware that onResume gets called even if the fragment has just been created, so be careful not to do that work more than once.
Either way, be sure to null your references to ensure your objects are marked for GC.
The FragmentManager will decide if it needs to discard and recreate the fragment as necessary in order to allow the user to go back to the fragment you added to the stack. In conditions where there's very little else on the stack and/or there's lots of spare memory it will probably just keep a direct reference to the fragment you added to the back stack.
Given all that, you also need to be careful about keeping references to other fragments, activities, etc as that kind of state is difficult to recreate.
The following approach is recommended for providing proper back navigation:
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
More info:
http://developer.android.com/guide/components/fragments.html
http://developer.android.com/training/implementing-navigation/temporal.html
API docs: http://developer.android.com/reference/android/app/Fragment.html

onSaveInstanceState called after fragment replaced

Problem
onSaveInstanceState is being called on device rotation for a fragment which has been replaced in a fragment transaction. This is causing an IllegalStateException with the message "Content view not yet created.
Research
I have found the following two answers which seem most relevant
Using onSaveInstanceState with fragments in backstack?
onSaveInstanceState of previous fragment is called on-orientation of current fragment
What I've tried already
Following the answer to the second question I have removed addtobackstack from the fragment transaction but am still getting the error.
I read the accepted answer to the first question but am not sure how to get a fragment reference within onSaveInstanceState for the activity. I also don't particularly want to save that fragment, when the user leaves that fragment it's state does not need to be saved.
I have also tried adding if (getView() != null) {...} to the onSaveInstanceState in the fragment but this made no difference.
Questions
Why is onSaveInstanceState being called even though the fragment hasn't been added to the back stack?
Is the right approach to somehow kill the fragment when the user is done with it?
Is there a different solution?
Thank you in advance for your help.
Andrew
Edit
If I remove addtobackstack(null) from the transaction which adds the fragment and the one where it is removed the problem goes away, but then so does an important piece of functionality... I could add that case to my onBackPressed override function but it seems a bit of a hack, and not in a good way.
Adding an isVisible() check onSaveInstanceState of the fragment in the backstack should work.
No need to retain instance state when it is not visible.

Android Fragment View State Loss When Using FragmentTransaction.replace()

I am having a pretty big issue and I am not quite understanding what is happening. I am developing an application that uses Fragments (from the support library) and am using FragmentTransaction.replace() to place new Fragments on to the back stack and replace the old one. The code looks as follows:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = ft.beginTransaction();
// Animations in my res/anim folder
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.fragment_container, newFragment, tag);
ft.addToBackStack(null);
ft.commit();
This is successful in replacing my fragment. My issue is the following. In one Fragment, I have a list of items that is built from user input. Now, when the user clicks next and then clicks the back button (to return to the list), the list is empty because the view is destroyed. Now, I have noted the following:
onSaveInstanceState is not called. I believe this is because that is only called when the parent Activity tells it to. Based on the docs: " There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.". Apparently, performing a replace on the FragmentTransaction is not one of those times. Does anyone have confirmation on this or a better explanation?
setOnRetainInstanceState(true) is not helpful in this situation. Again, I believe this has to do with info from the docs: "Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change)". I am not performing any action in re-creating the activity so this is of no use.
So, I guess my main question is: is there a way to preserve the View state (simply retain the Fragment) when using replace? There is FragmentTransaction.add(), but there are a few issues with this as well. One being that the exit animation is not performed, thus the animation is not correct. Another is that the new Fragment that the old fragment (the one that is being put into a non-visible state) is still clickable. For example, if I have a ListFragment, and I place a content fragment on top of that by using add, I can still click the list items in the ListFragment.
Without being able to see the code of your fragments this is a bit of a guess, but in the past I've run into this same issue and I've found that resetting the adapter in your ListFragment in onViewStateRestored seems to do the trick.
public void onViewStateRestored (Bundle savedInstanceState)
{
super.onViewStateRestored (savedInstanceState);
setListAdapter(new ArrayAdapter(Activity, R.layout.nav_item, objects));
}
Which is weird considering the documentation states that this method is called after onActivityCreated but before onStart. But it seems that it is also called at other times because when the most recent fragment transaction is popped off the back stack this method is called before the previously replaced fragment is displayed. The activity that owns the fragments has not been paused or obscured in any way, so according to the docs onViewStateRestored should not be called since just the fragments were modified. But this seems to work anyway.
It sounds like you simply need to make sure you have properly implemented onCreateView and onDestroyView. The situation you are describing seems to indicate that when the list fragment is put on the back stack (as a result of the replace transaction) Android is calling onDestroyView to free up some resources. However, it apparently has not destroyed the list fragment because when you tap back you are getting back the same instance of the fragment.
Assuming this is all true then, when the user taps back Android will call onCreateView. Any state that you have stored in the fragment's instance variables should still be there and all you need to do is repopulate the view...perhaps set the adapter on the ListView or whatever.
Also make sure your onSaveInstanceState() callback actually does save any instance state that you need to rebuild the view. That way if the fragment actually does get completely destroyed the FragmentManager can restore the state when it needs to recrete the fragment later.

Fragment backstack bug when replacing fragments in a transaction?

Here's the scenario that causes problems:
I start an Acitivity with a ViewGroup that'll hold the presented fragments.
I load Fragment A into this ViewGroup by calling .replace() in the transaction that I save onto the backstack.
I load Fragment B into the Viewgroup, replacing Fragment A. Again, I save the transaction.
I rotate the device twice.
On the second rotation, Fragment A (which is not visible at the moment) will throw a NullPointer exception.
This exception is thrown by Fragment A, because I'm saving some values of Views (EditTexts e.g.) in its onSaveInstanceState() method. It seems, that on the second rotation, the system doesn't instantiate these Views, so when I'm asking their values, I get a NullPointer exception. Can this be avoided somehow? Or is using .replace operations in a fragment transcation saved onto the backstack unadvised?
I've had this but can't quite recollect the specifics of what I did to fix but generally speaking (and apologies for the brain dump) I do the following:
Call setRetainInstance(true) in onCreate to avoid fragment recreation
Save the state of edit controls etc. in onSaveInstanceState to be used if activity is killed and you get restored with a non-null bundle (you shouldn't get a non-null bundle on an orientation change with 1.)
Maintain edit control values in member variables (as the fragment is not going to be recreated) ensuring they get updated in an onDestroyView from the edit controls and then use them to restore the edit control values in onCreateView
Have a flag which I set to true in onCreateView and false in onDestroyView and ensure I don't touch UI controls when the view is not around.
BTW Using replace while adding the transaction to the back stack is perfectly OK.
Hope there's something in there that helps. Peter.

Categories

Resources