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.
Related
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.
I have an Android app with TabManager. Due to change of Android API, I need to upgrade my app for Activity to contain the Fragment. To provide backward compatibility, I use ActionBarSherlock.
My App is working fine. However, looking at the Google Play Developer Console, there is always few crash reports on "java.lang.NullPointerException" on the line with getSherlockActivity(), I think only less than 0.1% out of total users are affected.
For example,
// Example 1
File file = new File(getSherlockActivity().getCacheDir(), "filename");
// Example 2
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false);
My question:
1. Should I change all the getSherlockActivity() to getActivity()? Or under certain rule, it is mandatory to use one of them?
2. What is the difference between them?
Thanks a lot.
The only difference is that with getSherlockActivity () you get the result of getActivity() but casted as a SherlockActivity. This allows to get access to the ABS specific apis.
If you just need something that is general enough to be in the Activity class, just use getActivity(). It will highlight this, otherwise highlight the fact that you use something that is ABS specific using getSherlockActivity ().
The NPE can come from :
using getActivity() (or siblings...) before onAttach has been executed
using getActivity() (or siblings...) after onDetach has been executed
So the solution is to check if your fragment is attached before using its activity :
if( isAttached() ) {
getActivity()....
}
Same topic
The NullPointerException is normally
Caution: If you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.
This exception probably means that the code is being exectuted when the fragment is not attached to an activity.
The reference returned by getSherlockActivity() is set when the Fragment is attached, and then cleared (set to null) when the Fragment is detached. If your code tries to reference getSherlockActivity() before or after this, you would get a null-pointer.
getSherlockActivity is just a shortcut to:
(SherlockActivity) getActivity()
So in your case there is no problem to use getActivity() instead in Example 1 but in Example 2 it will not work since it is a method of SherlockActivity.
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.
I'm having a problem instantiating Fragments in my program using the Support Library implementation. Here's a brief description of the task I'm attempting to perform and some of my attempts which haven't yet borne fruit:
The UI of my application is subject to change to meet user preferences. In order to do this, I'm using a Fragment for each different layout and replacing the active Fragment in the UI at a given time as per the user's instructions. Here are some ways I've tried (and failed) to do this:
I've tried adding the Fragments as non-static inner classes in my Activity. This approach worked so long as the user did not rotate the device. As soon as the user rotated the device, the application crashed (this is true for Portrait -> Landscape rotation and for Landscape -> Portrait rotation). Upon checking the issue using the emulator, I was getting an InstantiationException. I checked SO for some help, which led me to:
Implement the Fragment as a static inner class. When the Fragment initiates, it will expand its layout, and then from later in the control flow of the Activity, I can do stuff to the Fragment's subviews (in particular, add listeners to the buttons). Unfortunately this didn't work because I couldn't refer to the Fragment's subviews using [frag_name].getView().findViewById(). Something about referencing static objects in a non-static context. Once again, I checked SO, which led me to:
Implement the Fragment as a separate class altogether from the Activity. This seems to be what the Dev docs on developer.android.com recommend. Upon doing this, everything seems to compile fine, but when I try to refer to the Fragment's subviews (once again, using [frag_name].getView().findViewById()), I get a NullPointerException. When I add System.out.println() statements across my code to find out exactly what is happening, I find that the print statement inside onCreateView in the fragment is never getting fired, which implies that onCreateView is never getting triggered.
So now, I'm stuck. What am I doing wrong? The precise implementation of this isn't as important as learning something from the experience so I can get better at Android development, so if seperate classes are better than static classes or vice-versa, I don't really care which I use.
Thanks.
Figured it out. Turns out that in order to do what I wanted, I had to register the Activity as a Listener to each of the Fragments and pass "ready to enable buttons" messages back and forth between the two. To anyone using this question for further research, the guide on how to do that is located on the Android Developer guide, here: http://developer.android.com/training/basics/fragments/communicating.html
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.