I have a fragment (F1) with a public method like this
public void asd() {
if (getActivity() == null) {
Log.d(\"yes\",\"it is null\");
}
}
The other answers that suggest keeping a reference to the activity in onAttach are just suggesting a bandaid to the real problem. When getActivity returns null it means that the Fragment is not attached to the Activity. Most commonly this happens when the Activity has gone away due to rotation or the Activity being finished but the Fragment still has some kind of callback listener registered. When the listener gets called if you need to do something with the Activity but the Activity is gone there isn't much you can do. In your code you should just check getActivity() != null
and if it's not there then don't do anything. If you keep a reference to the Activity that is gone you are preventing the Activity from being garbage collected. Any UI things you might try to do won't be seen by the user. I can imagine some situations where in the callback listener you want to have a Context for something non-UI related, in those cases it probably makes more sense to get the Application context. Note that the only reason that the onAttach
trick isn't a big memory leak is because normally after the callback listener executes it won't be needed anymore and can be garbage collected along with the Fragment, all its View's and the Activity context. If you setRetainInstance(true)
there is a bigger chance of a memory leak because the Activity field will also be retained but after rotation that could be the previous Activity not the current one.
Since Android API level 23, onAttach(Activity activity) has been deprecated. You need to use onAttach(Context context). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)
Activity is a context so if you can simply check the context is an Activity and cast it if necessary.
@Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity){
a=(Activity) context;
}
}
Another good solution would be using Android's LiveData with MVVM architecture. You would define a LiveData object inside your ViewModel and observe it in your fragment, and when LiveData value is changed, it would notify your observer (fragment in this case) only if your fragment is in active state, so it would be guaranteed that you would make your UI works and access the activity only when your fragment is in active state. This is one advantage that comes with LiveData
Of course when this question was first asked, there was no LiveData. I am leaving this answer here because as I see, there is still this problem and it could be helpful to someone.
I have solved my problem this way.I have passed getApplicationContext from the previous class which has already access of getApplicationContext.I have passed Inputstream object to my new class Nutrients.
try{
InputStream is= getApplicationContext().getAssets().open("nutrient_list.json");
Nutrients nutrients=Nutrients.getNutrients(topRecognition,is);
} catch (IOException e) {
e.printStackTrace();
}
Do as follows. I think it will be helpful to you.
private boolean isVisibleToUser = false;
private boolean isExecutedOnce = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_my, container, false);
if (isVisibleToUser && !isExecutedOnce) {
executeWithActivity(getActivity());
}
return root;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
if (isVisibleToUser && getActivity()!=null) {
isExecutedOnce =true;
executeWithActivity(getActivity());
}
}
private void executeWithActivity(Activity activity){
//Do what you have to do when page is loaded with activity
}
This happened when you call getActivity()
in another thread that finished after the fragment has been removed. The typical case is calling getActivity()
(ex. for a Toast
) when an HTTP request finished (in onResponse
for example).
To avoid this, you can define a field name mActivity
and use it instead of getActivity()
. This field can be initialized in onAttach() method of Fragment as following:
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity){
mActivity =(Activity) context;
}
}
In my projects, I usually define a base class for all of my Fragments with this feature:
public abstract class BaseFragment extends Fragment {
protected FragmentActivity mActivity;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity){
mActivity =(Activity) context;
}
}
}
Happy coding,