208

I have a fragment (F1) with a public method like this

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

and yes when I call it (from the Activity), it is null...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

It must be something that I am doing very wrong, but I don't know what that is.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
Lukap
  • 31,523
  • 64
  • 157
  • 244
  • Where does the second code fragment belong to? To the oncreate()-method of the Activity? And have you already called setContentView() ? – Franziskus Karsunke Jun 02 '11 at 13:44
  • the problem it is not in the layouts, the app works good but why I get null for the getActivity ?, btw all the elements including the fragment it is rendered like it should not issues here – Lukap Jun 02 '11 at 13:59
  • I'm not sure if there was just an error when you pasted it into this post, but you need parenthesis after `getActivity()`. Also, how are you instantiating the fragment? Do you have it in your layout.xml? – CaseyB Jun 02 '11 at 13:41
  • R.id.upperPar is an element in the layout, so it supposed to be replaced with the fragment, but that is not my problem. I do not understand why I get null when I call getActivity() in custom fragment methods, let say in onActivityCreated method getActivity is the actual activity not null – Lukap Jun 02 '11 at 13:58
  • 1
    You should call this method: f1.asd(); in the onActivityCreated method which is to be overridden in your fragment class. – Namrata Bagerwal May 01 '17 at 06:53

15 Answers15

172

commit schedules the transaction, i.e. it doesn't happen straightaway but is scheduled as work on the main thread the next time the main thread is ready.

I'd suggest adding an

onAttach(Activity activity)

method to your Fragment and putting a break point on it and seeing when it is called relative to your call to asd(). You'll see that it is called after the method where you make the call to asd() exits. The onAttach call is where the Fragment is attached to its activity and from this point getActivity() will return non-null (nb there is also an onDetach() call).

Milad Faridnia
  • 9,113
  • 13
  • 65
  • 78
PJL
  • 18,735
  • 17
  • 71
  • 68
  • 5
    I didn't understand how you can solve your problem. If my getActivity() isn't stille ready, how can I get the reference of FragmentActivity object? – CeccoCQ Aug 08 '11 at 10:12
  • 1
    @Cecco, I don't quite understand your question or why you don't think this solves the problem. Until, the Fragment is attached to the (Fragment)Activity then getActivity() is going to return null and this is the case in `asd()`. – PJL Aug 10 '11 at 10:03
  • 1
    @PJL If I am performing some action in method asd() (e.g. showing dialog) does I need to wait until `onAttach(Activity activity)` get called or I have some other options(e.g. passing activity context by some other way). – Vivek Mar 27 '12 at 11:43
  • 2
    @Vivek I don't know quite what you want to achieve. If you need the Fragment to display a dialog straightaway then have it do what it needs to do on creation, e.g. in its `onCreateView` or `onActivityCreated` methods. I question why asd() needs to be called when it does in the questions posting. – PJL Mar 27 '12 at 12:47
  • @PJL Let me clear, Suppose I have two fragments (say f1 and f2) on click of f1, f1 will send some data to f2 and on that information f2 will download data from network. For doing this what I did On click of f1, I have written method in an Activity class, in that method I am adding fragment f2 in the activity, after that immediately I am calling a method which is present in f2, from that method I am downloading data from network until that I want to show progress dialog but It is giving null pointer exception because above reason(asynchronous call of `transaction1.commit();`). – Vivek Apr 01 '12 at 06:08
  • 1
    @Vivek Given that as soon as you add f2 you call it to download data consider having f2 download data from the network from its `onActivityCreated` method say at which point the activity is attached to the fragment. If you really want to do it from your activity then posting a runnable should suffice so that the fragment is committed by the time the runnable gets run. I'd prefer the former. – PJL Apr 01 '12 at 10:57
  • what is wanted to use object in setUserVisibleHint, it called before fragment onAttach get called..any suggestion? – CoDe Aug 05 '14 at 12:11
  • Are we leaking the `Activity` if we don't nullify it in `onDestroy()` or `onDetach()`? – Mohammed Ali Aug 26 '15 at 17:22
  • Usually this problem happens because of multithreading when you call getActivity() after it was detached, unfortunately it is not always so simple. It can happen and don't ask me how, that your fragment is between onAttach and onDetach and your getActivity() still returns null, so you have to keep your own activity reference set in onAtach. – Lukas Hanacek Apr 01 '16 at 07:00
  • 5
    onAttach deprecated – abbasalim Aug 18 '16 at 07:36
  • 8
    onAttach(Activity mActivity) seems to be depreciated.. any workaround for this – ashish.n Feb 27 '17 at 11:42
  • 1
    @ashish.n use onAttach(Context context) instead. – Phoenix Wang Mar 01 '17 at 16:55
  • 6
    API 24 introduced `commitNow()` – Nicolas Mar 04 '17 at 16:47
  • 1
    it doesn't matter onAttach is deprecated. The entire fragment design paradigm is deprecated as of oreo. – John Lord Nov 27 '18 at 19:04
  • is that true? I've been looking around and it still looks like a thing, so what replaces it? – stu Sep 19 '20 at 00:35
  • Hard to see when something is written and if it is still valid, but this sites says "As per the error message, only the framework android.app.Fragment classes are deprecated. The AndroidX Fragments are not deprecated and are continuing to have new releases." https://www.itcodar.com/android/android-fragment-onattach-deprecated.html#:~:text=The%20AndroidX%20Fragments%20are%20not%20deprecated,and%20are%20continuing%20to%20have%20new%20releases. – Ted Dec 27 '22 at 16:06
97

The best to get rid of this is to keep activity reference when onAttach is called and use the activity reference wherever needed, for e.g.

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
Pawan Maheshwari
  • 15,088
  • 1
  • 48
  • 50
  • 35
    Should we set mActivity = null onDetach() ? – Oliver Pearmain Apr 23 '14 at 10:54
  • 5
    @OliverPearmain if you will do it in onDetach() then there will no profit. You have to nullify it in onDestory(). Furthermore you have to hold it in WeakRefernce. – Kirill Popov Apr 29 '15 at 15:58
  • I am nullifying it in both `onDestroy()` and `onDetach()` because `onDestroy()` is not guaranteed to be called. – Mohammed Ali Aug 26 '15 at 17:20
  • 9
    Are we leaking the `Activity` if we don't nullify it in `onDestroy()`? – Mohammed Ali Aug 26 '15 at 17:21
  • 4
    According to http://developer.android.com/intl/zh-tw/guide/components/fragments.html, the onAttach() is called before calling the onCreateView(). But I still get a NullPointerException while I'm calling getActivity() in onCreateView(). How could that happen? – Kimi Chiu Dec 18 '15 at 02:16
  • @Kimi Chi, I have the same problem. Also onAttach(Activity activity) is depreciated on Android support v4 – oneNiceFriend Mar 01 '16 at 13:47
  • I would propose to not set `mContext = null`. Imagine you have a "long" running task which invokes the invoking Fragment with a callback method. In the callback method, another class is invoked where a reference to the context is passed. While the task is running I closed the fragment, but the task proceeds running. If I would set mContext = null, then the last class would crash with NPE since the context is null then. If I attach the context to mContext without detaching, the whole process is able to finish without NPE. – Bevor Oct 26 '18 at 14:55
  • @KimiChiu I'm new to Android. I think that in many cases, the parent `Activity` is not created (its `onCreate()` is still running), but the fragment's `onCreateView()` is already called. For example, this happens for me when I declare a static fragment (fragment in XML). Perhaps your 5 years of further experience can shed some light on this? – Minh Nghĩa Jun 29 '20 at 09:45
  • no matter if you mContext = null; in onDestroy() as long as not mContext var is NOT static var – erik.aortiz Oct 25 '22 at 18:18
93

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,

thanhbinh84
  • 17,876
  • 6
  • 62
  • 69
thucnguyen
  • 1,706
  • 15
  • 14
  • 21
    shall we set mActivity=null; in onDetach()? – Bharat Dodeja Apr 16 '15 at 16:38
  • thucnguyen, how about to declare mActivity static for single activity app? – iamthevoid Nov 26 '15 at 13:47
  • I don't see a reason to access `Activity` from another thread at all. You cannot do anything with it anyway, not even show a Toast. So you either should transfer work to main thread first, or not use Activity at all. – Dzmitry Lazerka Dec 19 '15 at 04:55
  • 4
    @BharatDodeja should we set mActivity = null onDetach? Did you find out? – SLearner Jun 22 '16 at 16:21
  • So, when the fragment is destroyed, mActivity is still there and can be referenced. And hence, in the callback to the function that had returned from another thread, mActivity can be referenced and used. Am I right? – SLearner Jun 22 '16 at 16:26
  • Yes, mActivity will be destroyed by the garbage collection (GC) later – thucnguyen Jun 23 '16 at 08:44
  • 9
    This is going to leak without nulling out activity. – Darek Deoniziak Jul 02 '16 at 05:34
  • @DzmitryLazerka, for instance, when you use a context for SharedPreferences you have to call an activity, but after a request getActivity() is null. – CoolMind Jan 25 '17 at 16:50
  • @CoolMind You can pass only the SharedPreferences instance to background threads. – Dzmitry Lazerka Feb 18 '17 at 06:06
  • @DzmitryLazerka, I meant that after return from a HTTP-request you could further switch to the main thread and try to access an activity. At least, in Retrofit you always get a result in the main thread. – CoolMind Feb 20 '17 at 20:50
  • onAttach(Activity mActivity) seems to be depreciated.. any workaround for this – ashish.n Feb 27 '17 at 11:42
54

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 preventing it from being garbage collected. 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.

miguel
  • 16,205
  • 4
  • 53
  • 64
  • 3
    This is exactly my problem. I have a fragment that does a process -> then an ad is shown -> and then the proess continues. In some devices after returning from the ad (through a listener to the ad events) getActivity() is null. But I need to continue doing the other part of the work to finish the job. Do you mean there's no solution to this? – Notbad May 29 '17 at 13:25
  • This is exactly what I am facing. I have an Activity interface in a fragment where I do some billing things. After the payment is done, I want to use the interface to do something, but the interface has been null. – Freddie Jan 25 '19 at 00:27
  • This seems to be the correct general answer to 100s of SO questions to this topic. – Manuel May 24 '19 at 00:32
  • 1
    Best answer. There's so many bandaid solutions for Android on SO. – maxbeaudoin Oct 08 '19 at 00:43
  • So If I want to do some operation, how can I execute after the getActivity() is available (if at all it is). – Sreekanth Karumanaghat Nov 21 '19 at 06:58
  • @miguel Is this going to leak even if we null out the activity reference onDetach() ? – Sreekanth Karumanaghat Nov 21 '19 at 09:51
  • There's no point in nulling in onDetach because you'd be replicating what getActivity() already does. You'd just be replacing `if (getActivity() != null)` with `if (mActivity != null) ` – miguel Nov 22 '19 at 00:56
20

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;
    }

}
Yuliia Ashomok
  • 8,336
  • 2
  • 60
  • 69
Sachin
  • 309
  • 1
  • 3
  • 9
10

PJL is right. I have used his suggestion and this is what i have done:

  1. defined global variables for fragment:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. implemented

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3 . I wrapped up my function, where I need to call getActivity(), in thread, because if it would run on main thread, i would block the thread with the step 4. and onAttach() would never be called.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4 . in my function where I need to call getActivity(), I use this (before the call getActivity())

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

If you have some UI updates, remember to run them on UI thread. I need to update ImgeView so I did:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});
zajac.m2
  • 1,218
  • 14
  • 13
7

The order in which the callbacks are called after commit():

  1. Whatever method you call manually right after commit()
  2. onAttach()
  3. onCreateView()
  4. onActivityCreated()

I needed to do some work that involved some Views, so onAttach() didn't work for me; it crashed. So I moved part of my code that was setting some params inside a method called right after commit() (1.), then the other part of the code that handled view inside onCreateView() (3.).

Bogdan Zurac
  • 6,348
  • 11
  • 48
  • 96
4

I am using OkHttp and I just faced this issue.


For the first part @thucnguyen was on the right track.

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).

Some HTTP calls were being executed even after the activity had been closed (because it can take a while for an HTTP request to be completed). I then, through the HttpCallback tried to update some Fragment fields and got a null exception when trying to getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO the solution is to prevent callbacks to occur when the fragment is no longer alive anymore (and that's not just with Okhttp).

The fix: Prevention.

If you have a look at the fragment lifecycle (more info here), you'll notice that there's onAttach(Context context) and onDetach() methods. These get called after the Fragment belongs to an activity and just before stop being so respectively.

That means that we can prevent that callback to happen by controlling it in the onDetach method.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    // Initialize HTTP we're going to use later.
    http = new OkHttpClient.Builder().build();
}

@Override
public void onDetach() {
    super.onDetach();

    // We don't want to receive any more information about the current HTTP calls after this point.
    // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
    for (Call call : http.dispatcher().queuedCalls()) {
        call.cancel();
    }
    for (Call call : http.dispatcher().runningCalls()) {
        call.cancel();
    }
}
zurfyx
  • 31,043
  • 20
  • 111
  • 145
3

Where do you call this function? If you call it in the constructor of Fragment, it will return null.

Just call getActivity() when the method onCreateView() is executed.

Dev-iL
  • 23,742
  • 7
  • 57
  • 99
Phạm Lam
  • 378
  • 1
  • 3
  • 14
1

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

}
Vinil Chandran
  • 1,563
  • 1
  • 19
  • 33
1

Those who still have the problem with onAttach(Activity activity), Its just changed to Context -

    @Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.context = context;
}

In most cases saving the context will be enough for you - for example if you want to do getResources() you can do it straight from the context. If you still need to make the context into your Activity do so -

 @Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity a; //Your activity class - will probably be a global var.
    if (context instanceof mActivity){
        a=(mActivity) context;
    }
}

As suggested by user1868713.

Shai
  • 318
  • 6
  • 15
0

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.

0

Call getActivity() method inside the onActivityCreated()

MuM6oJuM6o
  • 107
  • 10
0

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();
                  }
RAHUL KUMAR
  • 1,123
  • 11
  • 9
0

Write a common method that will ensure you will never get null Activity.

public class BaseFragment extends Fragment {
    private Context contextNullSafe;

     @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         /*View creation related to this fragment is finished here. So in case if contextNullSafe is null
         * then we can populate it here.In some discussion in - https://stackoverflow.com/questions/6215239/getactivity-returns-null-in-fragment-function
         * and https://stackoverflow.com/questions/47987649/why-getcontext-in-fragment-sometimes-returns-null,
         * there are some recommendations to call getContext() or getActivity() after onCreateView() and
         * onViewCreated() is called after the onCreateView() call every time */
        if (contextNullSafe == null) getContextNullSafety();
    }


   @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        contextNullSafe = context;
    }

    /**CALL THIS IF YOU NEED CONTEXT*/
    public Context getContextNullSafety() {
                if (getContext() != null) return getContext();
                if (getActivity() != null) return getActivity();
                if (contextNullSafe != null) return contextNullSafe;
                if (getView() != null && getView().getContext() != null) return getView().getContext();
                if (requireContext() != null) return requireContext();
                if (requireActivity() != null) return requireActivity();
                if (requireView() != null && requireView().getContext() != null)
                    return requireView().getContext();
                
                return null;
            
        }

    /**CALL THIS IF YOU NEED ACTIVITY*/
    public FragmentActivity getActivityNullSafety() {
        if (getContextNullSafety() != null && getContextNullSafety() instanceof FragmentActivity) {
            /*It is observed that if context it not null then it will be
             * the related host/container activity always*/
            return (FragmentActivity) getContextNullSafety();
        }
        return null;
    }
Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42