Why do Fragments detach from their Activity? - android

Like many others, I'm struggling with this error:
java.lang.IllegalStateException: Fragment xyz not attached to Activity
This question offers some ideas on how to deal with this. There are no explanations to the question however, why does a Fragment detach from their Activity in the first place? Does an understanding of why this happens help me to design my app in a way to avoid this happening?
In may case I do not have some asynchronous task and call getResources() when it completes; I call getResources() in the onCreate() method of the Fragment. And sometimes, when I'm navigating my app rather fast, said error surfaces. Is it to be expected that the Fragment is not even necessarily attached to its Activity during its own onCreate() method?
Secondly, the solutions provided in the linked question (guarding getResources() with isAdded() and getActivity() != null) don't help me. There is no reasonable way to deal with getResources() not being available.

Because Android supposes that Activities may be destroyed and recreated to accommodate configuration changes. Fragments, in contrast, are not.
Is it to be expected that the Fragment is not even necessarily attached to its Activity during its own onCreate() method?
Yes, it is "expected." Bad design IMO, but expected.

Related

android sliding menu getActivity returns null

I've been searching for this for a while now and haven't found any solution so far,
so I have a sliding menu, and withing it I call getActivity(), so far so good, but when I get out of the app (home button) and come back in a few minutes, the getActivity() returns nullpointerexception
if anyone has a clue about this it would be much appreciated
thanks
You have to be careful about exactly when you call getActivity(). It will return null in any call triggered by the parent activity's lifecycle all the way through onResume(). You have to make sure it is already attached to the parent activity before you can safely call it. Take a look at
http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResumeFragments()
Perhaps it will help you resolve your problem.
Depending on what you're trying to do, I would look into saving a pointer to the activity in the fragment so it can be referenced at times when getActivity might be null. That said, this could be kinda dangerous so be careful if that is the route you choose to take

Android - Memory pressure crashes with ViewPager > Fragments > Activities

I am using the v4 Support library in our app, and I have a step-based process in the app that is working fine under normal circumstances. However, we have a requirement that everything needs to work and not crash under memory pressure situations. So I'm using the SetAlwaysFinish library to help with this (https://github.com/bricolsoftconsulting/SetAlwaysFinish). It's been extremely helpful in pinpointing areas the need to detect and handle these kinds of situations, but I've run into one that has me stumped. Keep in mind this works fine under normal circumstances, but I'm explicitly testing with the "setAlwaysFinish" = ON.
Here's my setup: I have a "ProcessActivity" class that hosts the layout for a ViewPager. The ViewPager has an Adapter set, which contains my list of Fragments that will encompass the process. This is in the onCreate() method:
ProcessActivity:
createSteps(); // this creates/populates the ArrayList "processSteps" with Fragments
theAdapter = new ProcessAdapter(this, processSteps); // the ProcessAdapter class extends FragmentStatePagerAdapter, with an additional list of Fragment steps
theViewPager.setAdapter(theAdapter);
There are other pieces, but that is the core of how it's setup. During one of the Fragment steps, I actually need to "break" from the step process and push out to an Activity temporarily to do some logic (and then subsequently return to the step process). I do this as follows, with a ForResult, since I need to be able to handle an OK/Cancel action in the activity when it finishes:
Step4Fragment:
Intent intent = new Intent(getActivity(), ThePushActivity.class);
intent.putExtra(blah..);
startActivityForResult(intent, THE_REQCODE);
Once it pushed onto this view, sometimes the onDestroy() method of both the ProcessActivity and Step4Fragment are called (due to the alwaysFinish). Sometimes it seems to work fine and it calls back into the Step-Fragment process. But usually, what it does is that it will call the ProcessActivity's onDestroy() method, and then it will re-call the onCreate() method with the bundle saved-instance-state populated. This will call the step-creation code above, and it puts the app in a funky state where the last step the user was on is showing, but behind the scenes it's actually on the first step (fragments are, at that point, out of whack and disconnected), and a crash inevitably occurs. It seems at this point, the Step4Fragment is completely disjointed and will crash somewhere if you try to do anything, even though it seems as if it got re-instantiated correctly
Any thoughts on best ways to address this? I think it's fine if I find a way to even just reset things so that it kicks the user back to the first step of the process if a memory issue is occurring. However, my whole concern is the crash, of course.
Let me know if I need to provide more specifics. Thanks in advance for any help!
UPDATE #1:
Just a quick update that I've noticed that the Fragments are re-instantiated and initialized, and it properly falls into the "current" Fragment's onActivityResult() method and does what it needs to do properly. It seems where the disconnect lies is in the base ProcessActivity class, following one of these scenarios. The ViewPager layout shows properly, but everything outside of the ViewPager is not correct (i.e. it is indicating that it's on the 1st step of the process, when it should indicate that it's on the 4th, and the navigation buttons are showing for the 1st step rather than the 4th).
So I'm guessing I need to somehow properly set those elements manually. In digging deeper into this, I may be leaving out pieces of code that are needed for someone to actively help with this. If I could somehow access the state of a ViewPager field, which contains the ability to get the "currently shown fragment", before this whole onDestroy()/onCreate() was called, possibly from the savedInstanceState bundle? That would probably solve my issue. But from inspecting this bundle upon debugging, I pretty much only see the Fragments themselves, and their respective states. I'll keep digging, though.
Regardless, please let me know if anyone has any ideas on how this kind of thing should properly be done (at a high level, of course).
UPDATE #2
I'm seeing that even tho the "current" Fragment seems to be initiated correctly, and the view shows correctly, everything is detached. I can't call any methods on getResources() or getActivity(), etc. I actually was able to get the ProcessActivity 'working' properly based on saving off the step index (int) into the savedInstanceState bundle, and then reloading the UI elements around it. However, I still have this blocker with the current Fragment being detached from the Activity, even though it's seemingly re-instantiated properly.
UPDATE #3
When I follow the direction of this post: ViewPager and fragments — what's the right way to store fragment's state?, I end up with an immediate exception when I try to do the first putFragment(). The exception is: "IllegalStateException: Fragment Step4Fragment is not currently in the FragmentManager". I'm thinking this may have to do with the fact that I'm only keeping one Fragment to the left and one fragment to the right 'active' at any one time (i.e. offScreenPageLimit).
Are you sure that your Fragment isn't being re-instantiated?
All subclasses of Fragment must include a public empty constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the empty constructor is not available, a runtime exception will occur in some cases during state restore.
Turns out that the solution to this was similar to the solution presented in this post: ViewPager and fragments — what's the right way to store fragment's state?
I had a couple things going on that needed to change. First off, I needed to move the Fragments to fields rather than temp variables. Secondly, I needed to avoid instantiating new Fragments each time in the onCreate(). Instead, it should try to pull them from the savedInstanceState as such:
fragment1 = (Fragment1Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment1Step.class.getName());
fragment2 = (Fragment2Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment2Step.class.getName());
etc...
and then right after this, I have subsequent checks for each of them; if they are null (i.e. coming in fresh and not from a saved instance), then they will be instantiated new:
if (fragment1 == null) {
fragment1 = new Fragment1Step();
}
if (fragment2 == null) {
fragment2 = new Fragment2Step();
}
In order for this to work, of course, these should also be saved off in the onSaveInstanceState() method:
try {
getSupportFragmentManager().putFragment(outState, Fragment1Step.class.getName(), fragment1);
} catch (Exception e) {
// do nothing
}
try {
getSupportFragmentManager().putFragment(outState, Fragment2Step.class.getName(), fragment2);
} catch (Exception e) {
// do nothing
}
etc...
The reason why I have these in try/catch blocks is because our ViewPager only has an offScreenPageLimit of 1, so some of these will throw exceptions upon the "putFragment()" call if they are not currently in the 'active' stack of Fragments being shown.
It's not extremely pretty, and there may be a better way to handle this.. but this seems to work fine with this now in place.

Android fragments without an activity. Safe to discard?

So basically I have updated my application from using just activities in a tabBar to using SherlockFragments in a supportActionBar. Not a big deal you would think, and so did I. Everything worked perfectly through all of my tests. However when I released the new version my users started complaining and reporting numerous crashes where fragments throw a NPE when calling getSherlockActivity().
I have read about this problem and it's a problem with restoring instance states and fragments that are no longer attached to any activity but still run for some reason. I haven't found a specific solution for my problem (since I'm using the actionBar to display my tabs instead of a ViewPager).
Now my question is if I can simply discard these fragments that return null when calling getSherlockActivity()? Because they aren't attached to any activity, are they also not visible?
If not, how could I solve this problem? I am already checking the FragmentManager for already existing fragments before creating them, but this isn't sufficient.
Please help me!
You don't need to discard anything. All necessary checks are needed to be done in a Fragment.
You should try to avoid using getSherlockActivity() before onAttach() is called (since it is called before onCreate(), should be no problems with this, but still) and after onDetach() is called.
if (isAdded() && !isDetached())
or
final SherlockActivity a = getSherlockActivity();
if (a != null)
Might work okay.

setRetainInstance fragment with UI Android

Ok, I created a Fragment with some UI (couple textboxes and stuff) and I used setRetainInstance since Im running an AsyncTask to query a server (request can only be sent once) and I need the result of the AsyncTask. So my question is:
Is it wrong to retain the whole fragment with the UI? I saw couple examples where people use an extra Fragment to use the setRetainInstance but.. is there anything wrong not using that extra one??
If there is an issue with using the setRetainInstance why is that? Couldn't find any info in the documentation regarding this.
Even if you use setRetainInstance(true), your Fragment will still recreate its views when you rotate (you will get a call to onDestroyView and then onCreateView). As long as you don't keep references to views past onDestroyView, there will not be any leaks of the old Activity. The best approach would be to explicitly null the references in onDestroyView, but your code in onCreateView would generally overwrite those references anyway.
There are many examples online (including some official ones) where people use a separate fragment (without a view) to retain data. Assuming what I said above is correct, then this is unnecessary (for the sake of preventing leaks). In many cases, you may end up with cleaner code/architecture if you use a separate fragment whose responsibility is just to handle the data and not worry about the UI.
You can check to see if you are leaking Activity contexts after rotating by using Eclipse MAT.
If you are locking your orientation then you should be fine. Otherwise you can end up with memory leaks if you retain widgets that are associated with a particular activity instance.

Fragment lifecycle with respect to it's activity

Situation
My activity waits on an Async operation and after it hears back from async operation, it needs to pass information to 2 fragments inside it.
Requirement
1. Both fragments need their onCreateView calls to be done for them to have their layouts loaded,
2. They need for themselves to be attached to their activity so that getActivity() works.
I wrote a setData() method in both the fragments and am looking for the "correct" place in the activity's lifecycle to invoke them.
onCreate() of the activity does not work, onStart() of the activity does not work and onStart() of the fragment does not work.
Nothing works, what am I missing here?
The official documentation for the Fragment lifecycle explains this clearly - please refer to it and then ask follow-up questions if something is unclear.
This Image will be helpful to understand both life cycles together.
As many people complaints and it is somewhat valid argument that this life cycle is too complicated, in Google I/O 2018,They have suggested to use Architecture component Framework. Please check this Docs
when you are at Activity2---->backpress--->Fragment2(Activity1)---means Activity1 again attach from fragment2 so on OnAactivityCreated() method Activity1 is completely loaded ....so at that we can call setData() method of your Activity1...
onAttachFragment()-activity is called before onCreate()-activity and after onAttach()-fragment
Call onDestroy on onStop of your fragment. This should call onCreate when the fragment is launched.
Let me know if works as an ideal solution for your problem.

Categories

Resources