As mentioned here:
"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."
I have a couple of questions regarding calling getActivity() inside DialogFragment.
What are different scenarios under which a DialogFragment can unexpectedly get detached from its parent Activity or didn't attach at the first place? The thing is I am calling getActivity() inside onPositiveButtonClick's listener and have received a couple of crash reports (Null pointer exception) for it. I am unable to reproduce the crash, screen orientation doesn't seem to do the trick.
What are some recommended guidelines on how to use getActivity() with minimal damage? I have read some other stackoverflow posts which suggest
a) Override onAttach() method.
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
I would prefer this less as it keeps an instance of the Activity. Also, how can I be sure local instance of the activity is never set to null. Would like to know the pros and cons before using it.
b) Will delegating the onClick() implementations to the calling activity using an interface help? If yes, how?
If all of this is unavoidable, I don't see a better alternative than letting the app crash. I can't show a toast since getActivity() is null and would avoid letting the onClick operation fail silently.
Any pointers would be much appreciated. Thank you!
Related
I am not much experienced in Android Programming. And Currently I am working on an Application which makes use of Fragments. Sometimes i see that getActivity() returns null randomly. It happens once in thousand times or so. For now it's fine because there are no much users using it. But when app will go live may create many issues.
So my question is what is the best programming practice to make sure getActivity() never returns null in any case?
I searched online but everybody talks about solving issue in their existing code. If anyone has a generalised way of explaining it, then please let me know. Thanks you so much in advance
It's not random, it means that your Fragment is not currently attached to your Activity.
There is a ton of answer on that topic on SO, that I don't consider to be a good practice, is to override onAttach() and keep a reference to your Activity, like that :
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
I think you should not do that and just check if getActivity() doesn't return null.
If you just need a Context reference you could however to that :
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mContext = activity.getApplicationContext();
}
And use you mContext which is the application Context.
PS:
If you run your application on a device with API 23, onAttach(Context) will be called. On previous APIs onAttach(Activity) will be called.
EDIT :
In that case you should use Loaders :
https://www.youtube.com/watch?v=s4eAtMHU5gI
http://developer.android.com/intl/es/guide/components/loaders.html
You should override onActivityCreated and call getActivity() from there, in this way you can be sure getActitity() never returns null, because called after parent activity onCreate has done.
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// call getActivity() here
}
Here is supporting quote from android doc(link):
public void onActivityCreated (Bundle savedInstanceState)
Called when the fragment's activity has been created and this fragment's view hierarchy instantiated. It can be used to do final initialization once these pieces are in place, such as retrieving views or restoring state. It is also useful for fragments that use setRetainInstance(boolean) to retain their instance, as this callback tells the fragment when it is fully associated with the new activity instance. This is called after onCreateView(LayoutInflater, ViewGroup, Bundle) and before onViewStateRestored(Bundle).
I have 3 fragments that are contained within the main activity via a ViewPager. What I'm trying to do is allow myself to call methods on objects of those fragment classes from different lifecycle callbacks in the main activity, namely onRestart(), so that I can refresh their content via a backend call. To be more specific, one of the fragments contains a "live feed" of images that people are posting, and the user gets to this posting activity via one of the fragments.
Right now, the problem I'm trying to solve is that when a user goes to this posting activity and puts up a picture, the live feed in the fragment they come back to isn't getting refreshed. To do this, I would need to call my backend and UI update methods in that fragment from the main activity's onRestart().
But I'm running into these null pointer exceptions -- here's what the code looks like in the fragment:
public void refreshUI(){
activity = getActivity();
appContext = ThisApp.appContext();
view = getView();
peopleHeader = (TextView) view.findViewById(R.id.people_header);
peopleLinearLayout = (LinearLayout) view.findViewById(R.id.local_people_linearlayout);
..... (various other instantiations)... }
The NPEs are coming either from getActivity() or some of the UI instantiations. I call this code from onActivityCreated() and it works as expected. However, if I try to call this code again on my fragment object from onRestart(), I crash.
What's happening? Can't figure out why this would go bad on me.
Thanks so much.
When your Activity is destroyed and recreated, your Fragments are also destroyed and recreated with it. You can have a look at the Fragment/Activity lifecycle explanation here.
What this means is that the reference you keep disappears, the Fragment is there but it is another object. You need to get a reference to this new object.
You can check this answer on how to do that. It explains getting references to Fragments created by a ViewPager adapter.
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.
I'm using fragments in my application. And the very common problem while using them are the NPE when using getActivity(). I know we can solve it by checking if getActivity() != null every single time or checking if the fragment isAdded().
In one of my classes I'm getting activity's context in more than 60 places. Checking if getActivity()is not null or if the fragment is still added to the activity in all the places is making the code ugly,larger and non-maintainable. Is there any other way to handle this?
Is it even possible to destroy the fragment(and stop any work it has been doing while being removed) when it is removed from the activity?
Also Is this way a suggested one?
In my experience, most cases of getActivity() returning null are in asynchronous callbacks.
For example, your fragment fires an AsyncTask, and then gets removed before the the background job is done, then when the background job finishes and calls getActivity() in onPostExecute(), it will get a null since the fragment is already detached from the activity.
My solution:
1.Check getActivity()==null at the beginning of every asynchronous callback, if it's the case then just abort the method.
2.Cancel asynchronous jobs in onDetach().
And I think this is a better solution than saving the activity instance in onAttach(), because since your fragment is removed, why bother doing all the jobs left in the callbacks(in most cases UI codes)?
getActivity will be reinitilized in method - onActivityCreated().
So it's safer to call getActivity() right after onActivityCreated() (according to lifecycle of fragments http://developer.android.com/guide/components/fragments.html) - for example in onStart() - in that case it's WILL BE NEVER NULL - no need to do useless checks like isAdded and getActivity != null.
P.S. If we use that solution:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
mActivity will be never null - but later in method onActivityCreated() getActivity() became different with mActivity.
My opinion - sure we can save whole activity in variable, but it's safer to follow android fragments lifecycle workflow and get activity right after onActivityCreated()
I think you should use the Fragment's onAttach(Activity) method.
I think that should help you avoid all of those NPE.
My solution is override the onSaveInstanceState method in BaseActivity:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//solution of fragment.getActivity() is null
outState.remove("android:support:fragments");
}
I didn't found a solution to that, maybe because if you think about the lifecycle of a fragment you should be able to understand when you have check the null value.
If the user e.g. changes device's orientation during the REST (or any other long running async operation), the fragment is detached from the activity.
So if this fragment uses getActivity() anywhere in the code handling the REST response, it will raise a null pointer.
I can protect all the calls of getActivity() with null checks - but there's still the possibility the activity becomes null between the check-line and the use-line. And also there's lot of places where this is done, so the code will become a mess.
setRetainInstance(true) is not usable if I want to change the layout on orientation change. + There's are also some strange effects with that like: Multiple fragments, setRetainInstance(true) and screen rotation
So this leads me to think maybe its generally bad practice to handle rest calls in fragments?
I have seen some practices where the activity contains a non visual fragment to handle the responses. But I can't put this in the fragments, I guess. So I have to use the activity as a mediator and reach the results to the current fragment?
I just thought, it's cleaner to put everything in the fragment. Since I don't need to modify the code elsewhere. And it's then a self contained entity, which I can put somewhere else without problems. But what do I do with these unreliable references to the context? I mean if the fragment is recreated, I really don't care about the detached fragment - it just has to finish whatever it's doing silently and don't disturb my new workflow with exceptions. And of course I don't want to surround everything with try catch(Exception) since I care about the exceptions thrown in other situations.
I'm usually use the fragment as the display aspect and the activity as the logic aspect. just like MVC- the fragment is the view and the activity is the controller.
So all my fragments has this code:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (FragmentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement FragmentListener");
}
}
and every activity that uses this fragment implement the FragmentListener interface.
Once I got a response from the server I'm just populate (or just calling the right method) the fragment, you can do some null checking here, but It will be rare...
You certainly can do that. You probably want to look at AsyncTaskLoader and the LoaderManager (they specifically cope with the lifecycle issues you are talking about). Also be aware that task loader IDs have to be unique to the activity, so if you have multiple fragments running loaders hosted within the same activity, the loader id's cant all be the same ;-)
on orientation change, the activity will be re-created. To protect this use the below code
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" in manifest file ( i.e in activity tag )
and ovrride the method in your MainActivity
#Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
}