0

I am getting errors on few devices where I am trying to display a toast inside fragments. this toast is usually on retrofit response failure. toast code is simple. Please suggest, could not find any reason searching here and there.

Toast.makeText(getActivity(), "Connection Failure", Toast.LENGTH_LONG).show();

and my ST logs are below.

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
   at android.widget.Toast.(Toast.java:103)
   at android.widget.Toast.makeText(Toast.java:256)
Imanali Mamadiev
  • 2,604
  • 2
  • 15
  • 23
Bharat Kumar
  • 457
  • 1
  • 5
  • 18
  • 2
    use getContext() instead of getActivity() – Syed Danish Haider Sep 12 '18 at 07:33
  • Hi Syed Danish Haider. thank you for your answer...would you please help me understand with a little explanation for your suggestion.... – Bharat Kumar Sep 12 '18 at 07:35
  • i have posted answer for your refrence,please accept my answer if its helpfull – Syed Danish Haider Sep 12 '18 at 07:39
  • 1
    Hold the app context in global variable and use that global variable instead of getActivity() – Kushal Sep 12 '18 at 08:29
  • 1
    @Kushal I like that - we must be thinking alike. I was adding that into my answer around the same time you posted this. I also included a link to the code where this is done using the Singleton pattern. Maybe not the best in all cases, but it is a great workaround for tricky cases like this. – Richard Le Mesurier Sep 12 '18 at 08:34

6 Answers6

4

According to the code & javadoc for Fragment.getActivity() you can get null returned:

/**
 * Return the {@link FragmentActivity} this fragment is currently associated with.
 * May return {@code null} if the fragment is associated with a {@link Context}
 * instead.
 *
 * @see #requireActivity()
 */
@Nullable
final public FragmentActivity getActivity() {
    return mHost == null ? null : (FragmentActivity) mHost.getActivity();
}

Particularly this could happen when your Fragment is not attached to an activity (as pointed out here and here).

Similarly, getContext() can also return null.

There's a good discussion on when these can be null on this possibly related post:


The simple solution has been provided already - put in a null check before displaying the Toast.

But the underlying problem is one of architecture - your code is coupling API activity to your UI, and assuming certain things about your UI state i.e. you are assuming that when the API call returns, your screen is still visible to the user.


A better solution would be to decouple the Retrofit call from the UI - put the API calls in a separate class that does not depend on the UI state.

Use an event or pub-sub framework to communicate from this API wrapper class back to any UI components that need to know when an API call returns.

EventBus or RxJava would be 2 common solutions for this (LocalBroadcastManager would be a less common approach).

This will allow any code to call into your API, and to subscribe to be notified when the API returns.

It also allows you to save your API responses in (for example) a local database, in which case you could just rely on the LiveData pattern to update any UI that needs to be.

Here's a Medium article giving a brief description of how to use the Android Architecture Components in this manner using the Repository pattern.


Given that some projects cannot be redesigned immediately, there may be need for workarounds.

The above mentioned null check workaround is useful in that the app will no longer crash. Unfortunately it does mean that the user will not be alerted to the failed API call.

One alternative is to create your own Application subclass (many projects will already have done this in order to initialise common libraries) and provide a method for static access to this application Context. (A similar suggestion has subsequently been made by Kushal.)

You could then choose to display the Toast using the application Context instead of the one from the fragment. You may lose any specific styling that would have been gained from the more specific context, but the advantage would be that your user still gets to see the Toast message.

Exposing your Application as a singleton has been described very nicely on this post:

Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
  • 2
    thank you for such a detailed explanation. I could relate reasons for crash/ANR reading it. Architectural changes might not be possible as of now. I will keep this with your suggestions for a future plan. – Bharat Kumar Sep 12 '18 at 08:07
  • 100% understand the difficulty or rearchitecting existing code, and that's why I linked specifically to the other answers - I have a lot of code that does that simple `if(x != null){}` check - but it seems a common misunderstanding that `getActivity()`/`getContext()` are always available so I thought a detailed answer to this type of question may be useful for future readers. – Richard Le Mesurier Sep 12 '18 at 08:11
  • @BharatKumar I've added another workaround so that you still get to display the Toast to the user by using the Application Context - this might be more useful than not displaying anything at all. – Richard Le Mesurier Sep 12 '18 at 08:26
3

Sometimes getActivity() or getContext() may produce a null pointer exception when the fragment is not associated with an Activity. So use the onAttach method

public class yourFragment extends Fragment {
   Context context

   @Override
   public void onAttach(Context context) {
        this.context = context;
        super.onAttach(context);
   }
}
Yasiru Nayanajith
  • 1,647
  • 17
  • 20
3

Maybe getActivity() called when fragment detached. Try it.

if (isAdded()) {
    Toast.makeText(getActivity(), "something", Toast.LENGTH_SHORT).show();
}
galcyurio
  • 1,776
  • 15
  • 25
2

Your activity context is null in this line :

Toast.makeText(getActivity(), "Connection Failure", Toast.LENGTH_LONG).show();// getActivity() is null

To avoid crash use this :

if(getActivity() != null)
   Toast.makeText(getActivity(), "Connection Failure", Toast.LENGTH_LONG).show();
Jeel Vankhede
  • 11,592
  • 2
  • 28
  • 58
0

Change your getActivity() to getContext(). try the given code below:

Toast.makeText(getContext(), "Connection Failure", Toast.LENGTH_LONG).show();
Rubick
  • 296
  • 3
  • 22
0
if (isAdded()) {
    Toast.makeText(getActivity(), "something", Toast.LENGTH_SHORT).show();
}

or try this

if(getActivity() != null)
   Toast.makeText(getActivity(), "Connection Failure", Toast.LENGTH_LONG).show();

Both will work fine in my case because in some case getActivity() called when fragment detached.

So we have to take care of that as well.

mini developer
  • 302
  • 3
  • 7