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.
Related
After updating the SDK to API level 23, I found that onAttach (Activity activity) is deprecated and the new method is onAttach (Context context). can any one enlighten me on why this change was made?
I think it has basically been to expand the scope of the method, but the official changelog doesn't say anything about it.
As you can see in the changelog they have removed the void onAttach(Activity) but they added a new one with the same name, and it says that is deprecated in the Android Official Documentation.
As richq commented, the support version of Fragment also deprecates onAttach(Activity) and has an onAttach(Context) that can be used instead on all Android versions right back to prehistoric ones.
To adapt to this new changes you can follow this steps:
Change the argument type of onAttach callback from Activity to Context. For unknown reason, this modification results in the fact that the method onAttach(Context) is not called anymore during the fragment lifecycle.
Move the code that was in onAttach method to onCreate one since it gets still executed.
With this modification, the app turns to run as before. No additional import statements are required.
Until this change happened, a fragment could only be attached to an activity. After this change Google can work towards attaching fragments to Services too. Something like how Facebook chat heads work, they could have fragment floating outside an activity too.
Calling setHasOptionsMenu(true) from constructor, which is obviously called even before onCreate(), works perfectly! Can I do that? What will be problems?
Check here fragment - Android
Applications should generally not implement a constructor. The first
place application code can run where the fragment is ready to be used
is in onAttach(Activity), the point where the fragment is actually
associated with its activity. Some applications may also want to
implement onInflate(Activity, AttributeSet, Bundle) to retrieve
attributes from a layout resource, though should take care here
because this happens for the fragment is attached to its activity.
You may want to use another lifecycle event for this.
Yes, you can call setHasOptionsMenu(true) from the constructor.
I tend to call getActivity().getApplicationContext() a lot in my fragments, would it be a bad idea to just to set a Context field variable by default on my fragments? We are starting android development at work and I don't want to create any bad habits for the group.
Its not recommended to use getApplicationContext() , just use the Activity context , also in some conditions getActivity() might return null in Fragments, so i think getting Activity Context on onAttach(Activity activity) is a better way in Fragments.
I don't see a reason to use the Application Context while you could use directly the Activity instance.
Please notice that if the fragment is detached from the activity the getActivity() will return you Null as a result. So you have to see the application logic.
If the fragment needs to do something even after it is detached from the activity then keep the activity as a field, otherwise simply check if (getActivity() != null)
One reason where I have seen it useful to store the application context as a field in a fragment is when the fragment is of the type that don't have any UI and it's purpose is to start a task that does something in the background, and the fragment also have the setRetainInstance(true); to be kept on rotation.
In that case the activity can be recreated on rotation but the task that do the background thing in the background can continue and is held by the fragment. Let's say the result of that task have fetched something from Internet and then want to store it in a database, then the callback for the task comes back to the fragment. A call to getActivity()could result in null due to a potential rotation, the application context is however kept so therefore a field with the application context is handy because it is not destroyed when the activity is. The getActivity().getApplicationContext() would need to be called in the onAttach() method and stored to a field.
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.
Is anyone aware of a way to check the existence of fragment using Robotium?
Regards
Rc
I'm assuming that you want to find the existence of a fragment that you know the activity or tag for.
first call getCurrentActivity() on Solo and then call
getFragmentManager().findFragmentById()
or
getSupportFragmentManager().findFragmentById()
if you're using the v4 compatibility
library. That will search all the fragments on the page and return it. You can also use findFragmentByTag() if you've previously tagged the Fragment.