Android. Fragment getActivity() sometimes returns null

AndroidAndroid FragmentsAndroid ActivityAndroid AsynctaskNullpointerexception

Android Problem Overview


In developer console error reports sometimes I see reports with NPE issue. I do not understand what is wrong with my code. On emulator and my device application works good without forcecloses, however some users get NullPointerException in fragment class when the getActivity() method is called.

Activity

pulic class MyActivity extends FragmentActivity{

    private ViewPager pager; 
    private TitlePageIndicator indicator;
    private TabsAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    	pager = (ViewPager) findViewById(R.id.pager);
		indicator = (TitlePageIndicator) findViewById(R.id.indicator);
    	adapter = new TabsAdapter(getSupportFragmentManager(), false);
		
	    adapter.addFragment(new FirstFragment());
        adapter.addFragment(new SecondFragment());
        indicator.notifyDataSetChanged();
        adapter.notifyDataSetChanged();

    	// push first task
	    FirstTask firstTask = new FirstTask(MyActivity.this);
		// set first fragment as listener
        firstTask.setTaskListener((TaskListener) adapter.getItem(0));
        firstTask.execute();
	}

	indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener()  {
        @Override
        public void onPageSelected(int position) {
            Fragment currentFragment = adapter.getItem(position);
            ((Taskable) currentFragment).executeTask();
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {}

        @Override
        public void onPageScrollStateChanged(int i) {}
    });
}

AsyncTask class

public class FirstTask extends AsyncTask{

    private TaskListener taskListener;

	...

    @Override
    protected void onPostExecute(T result) {
        ...	
        taskListener.onTaskComplete(result);
    }	
}

Fragment class

public class FirstFragment extends Fragment immplements Taskable, TaskListener{

	public FirstFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.first_view, container, false);
    }

	@Override
    public void executeTask() {
        FirstTask firstTask = new FirstTask(MyActivity.this);
        firstTask.setTaskListener(this);
        firstTask.execute();
    }

	@Override
    public void onTaskComplete(T result) {
	    // NPE is here 
        Resources res = getActivity().getResources();
    	...
    }
}

Maybe this error happens when applications resumed from background. In this case how I should handle this situation properly?

Android Solutions


Solution 1 - Android

It seems that I found a solution to my problem. Very good explanations are given here and here. Here is my example:

pulic class MyActivity extends FragmentActivity{

private ViewPager pager; 
private TitlePageIndicator indicator;
private TabsAdapter adapter;
private Bundle savedInstanceState;

 @Override
public void onCreate(Bundle savedInstanceState) {
	 
	.... 
	this.savedInstanceState = savedInstanceState;
	pager = (ViewPager) findViewById(R.id.pager);;
	indicator = (TitlePageIndicator) findViewById(R.id.indicator);
	adapter = new TabsAdapter(getSupportFragmentManager(), false);

	if (savedInstanceState == null){	
		adapter.addFragment(new FirstFragment());
		adapter.addFragment(new SecondFragment());
	}else{
		Integer  count  = savedInstanceState.getInt("tabsCount");
		String[] titles = savedInstanceState.getStringArray("titles");
		for (int i = 0; i < count; i++){
			adapter.addFragment(getFragment(i), titles[i]);
		}
	}
	
	
	indicator.notifyDataSetChanged();
	adapter.notifyDataSetChanged();

	// push first task
	FirstTask firstTask = new FirstTask(MyActivity.this);
	// set first fragment as listener
	firstTask.setTaskListener((TaskListener) getFragment(0));
	firstTask.execute();

}

private Fragment getFragment(int position){
	 return savedInstanceState == null ? adapter.getItem(position) : getSupportFragmentManager().findFragmentByTag(getFragmentTag(position));
}

private String getFragmentTag(int position) {
	return "android:switcher:" + R.id.pager + ":" + position;
}

 @Override
protected void onSaveInstanceState(Bundle outState) {
	super.onSaveInstanceState(outState);
	outState.putInt("tabsCount",      adapter.getCount());
	outState.putStringArray("titles", adapter.getTitles().toArray(new String[0]));
}

 indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
		@Override
		public void onPageSelected(int position) {
			Fragment currentFragment = adapter.getItem(position);
			((Taskable) currentFragment).executeTask();
		}

		@Override
		public void onPageScrolled(int i, float v, int i1) {}

		@Override
		public void onPageScrollStateChanged(int i) {}
 });

The main idea in this code is that, while running your application normally, you create new fragments and pass them to the adapter. When you are resuming your application fragment manager already has this fragment's instance and you need to get it from fragment manager and pass it to the adapter.

UPDATE

Also, it is a good practice when using fragments to check isAdded before getActivity() is called. This helps avoid a null pointer exception when the fragment is detached from the activity. For example, an activity could contain a fragment that pushes an async task. When the task is finished, the onTaskComplete listener is called.

@Override
public void onTaskComplete(List<Feed> result) {

    progress.setVisibility(View.GONE);
    progress.setIndeterminate(false);
    list.setVisibility(View.VISIBLE);

    if (isAdded()) {

        adapter = new FeedAdapter(getActivity(), R.layout.feed_item, result);
        list.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

}

If we open the fragment, push a task, and then quickly press back to return to a previous activity, when the task is finished, it will try to access the activity in onPostExecute() by calling the getActivity() method. If the activity is already detached and this check is not there:

if (isAdded()) 

then the application crashes.

Solution 2 - Android

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(context);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

Edited, since onAttach(Activity) is depreciated & now onAttach(Context) is being used

Solution 3 - Android

Ok, I know that this question is actually solved but I decided to share my solution for this. I've created abstract parent class for my Fragment:

public abstract class ABaseFragment extends Fragment{

    protected IActivityEnabledListener aeListener;

    protected interface IActivityEnabledListener{
        void onActivityEnabled(FragmentActivity activity);
    }

    protected void getAvailableActivity(IActivityEnabledListener listener){
        if (getActivity() == null){
            aeListener = listener;

        } else {
            listener.onActivityEnabled(getActivity());
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        if (aeListener != null){
            aeListener.onActivityEnabled((FragmentActivity) activity);
            aeListener = null;
        }
    }

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

        if (aeListener != null){
            aeListener.onActivityEnabled((FragmentActivity) context);
            aeListener = null;
        }
    }
}

As you can see, I've added a listener so, whenever I'll need to get Fragments Activity instead of standard getActivity(), I'll need to call

 getAvailableActivity(new IActivityEnabledListener() {
        @Override
        public void onActivityEnabled(FragmentActivity activity) {
            // Do manipulations with your activity
        }
    });

Solution 4 - Android

Don't call methods within the Fragment that require getActivity() until onStart in the parent Activity.

private MyFragment myFragment;


public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    myFragment = new MyFragment();

    ft.add(android.R.id.content, youtubeListFragment).commit();

    //Other init calls
    //...
}


@Override
public void onStart()
{
    super.onStart();
   
    //Call your Fragment functions that uses getActivity()
	myFragment.onPageSelected();
}

Solution 5 - Android

I've been battling this kind of problem for a while, and I think I've come up with a reliable solution.

It's pretty difficult to know for sure that this.getActivity() isn't going to return null for a Fragment, especially if you're dealing with any kind of network behaviour which gives your code ample time to withdraw Activity references.

In the solution below, I declare a small management class called the ActivityBuffer. Essentially, this class deals with maintaining a reliable reference to an owning Activity, and promising to execute Runnables within a valid Activity context whenever there's a valid reference available. The Runnables are scheduled for execution on the UI Thread immediately if the Context is available, otherwise execution is deferred until that Context is ready.

/** A class which maintains a list of transactions to occur when Context becomes available. */
public final class ActivityBuffer {

    /** A class which defines operations to execute once there's an available Context. */
    public interface IRunnable {
        /** Executes when there's an available Context. Ideally, will it operate immediately. */
        void run(final Activity pActivity);
    }

    /* Member Variables. */
    private       Activity        mActivity;
    private final List<IRunnable> mRunnables;

    /** Constructor. */
    public ActivityBuffer() {
        // Initialize Member Variables.
        this.mActivity  = null;
        this.mRunnables = new ArrayList<IRunnable>();
    }

    /** Executes the Runnable if there's an available Context. Otherwise, defers execution until it becomes available. */
    public final void safely(final IRunnable pRunnable) {
        // Synchronize along the current instance.
        synchronized(this) {
            // Do we have a context available?
            if(this.isContextAvailable()) {
                // Fetch the Activity.
                final Activity lActivity = this.getActivity();
                // Execute the Runnable along the Activity.
                lActivity.runOnUiThread(new Runnable() { @Override public final void run() { pRunnable.run(lActivity); } });
            }
            else {
                // Buffer the Runnable so that it's ready to receive a valid reference.
                this.getRunnables().add(pRunnable);
            }
        }
    }

    /** Called to inform the ActivityBuffer that there's an available Activity reference. */
    public final void onContextGained(final Activity pActivity) {
        // Synchronize along ourself.
        synchronized(this) {
            // Update the Activity reference.
            this.setActivity(pActivity);
            // Are there any Runnables awaiting execution?
            if(!this.getRunnables().isEmpty()) {
                // Iterate the Runnables.
                for(final IRunnable lRunnable : this.getRunnables()) {
                    // Execute the Runnable on the UI Thread.
                    pActivity.runOnUiThread(new Runnable() { @Override public final void run() {
                        // Execute the Runnable.
                        lRunnable.run(pActivity);
                    } });
                }
                // Empty the Runnables.
                this.getRunnables().clear();
            }
        }
    }

    /** Called to inform the ActivityBuffer that the Context has been lost. */
    public final void onContextLost() {
        // Synchronize along ourself.
        synchronized(this) {
            // Remove the Context reference.
            this.setActivity(null);
        }
    }

    /** Defines whether there's a safe Context available for the ActivityBuffer. */
    public final boolean isContextAvailable() {
        // Synchronize upon ourself.
        synchronized(this) {
            // Return the state of the Activity reference.
            return (this.getActivity() != null);
        }
    }

    /* Getters and Setters. */
    private final void setActivity(final Activity pActivity) {
        this.mActivity = pActivity;
    }

    private final Activity getActivity() {
        return this.mActivity;
    }

    private final List<IRunnable> getRunnables() {
        return this.mRunnables;
    }

}

In terms of its implementation, we must take care to apply the life cycle methods to coincide with the behaviour described above by Pawan M:

public class BaseFragment extends Fragment {

    /* Member Variables. */
    private ActivityBuffer mActivityBuffer;

    public BaseFragment() {
        // Implement the Parent.
        super();
        // Allocate the ActivityBuffer.
        this.mActivityBuffer = new ActivityBuffer();
    }

    @Override
    public final void onAttach(final Context pContext) {
        // Handle as usual.
        super.onAttach(pContext);
        // Is the Context an Activity?
        if(pContext instanceof Activity) {
            // Cast Accordingly.
            final Activity lActivity = (Activity)pContext;
            // Inform the ActivityBuffer.
            this.getActivityBuffer().onContextGained(lActivity);
        }
    }

    @Deprecated @Override
    public final void onAttach(final Activity pActivity) {
        // Handle as usual.
        super.onAttach(pActivity);
        // Inform the ActivityBuffer.
        this.getActivityBuffer().onContextGained(pActivity);
    }

    @Override
    public final void onDetach() {
        // Handle as usual.
        super.onDetach();
        // Inform the ActivityBuffer.
        this.getActivityBuffer().onContextLost();
    }

    /* Getters. */
    public final ActivityBuffer getActivityBuffer() {
        return this.mActivityBuffer;
    }

}

Finally, in any areas within your Fragment that extends BaseFragment that you're untrustworthy about a call to getActivity(), simply make a call to this.getActivityBuffer().safely(...) and declare an ActivityBuffer.IRunnable for the task!

The contents of your void run(final Activity pActivity) are then guaranteed to execute along the UI Thread.

The ActivityBuffer can then be used as follows:

this.getActivityBuffer().safely(
  new ActivityBuffer.IRunnable() {
    @Override public final void run(final Activity pActivity) {
       // Do something with guaranteed Context.
    }
  }
);

Solution 6 - Android

@Override
public void onActivityCreated(Bundle savedInstanceState) {
	super.onActivityCreated(savedInstanceState);
	// run the code making use of getActivity() from here
}

Solution 7 - Android

I know this is a old question but i think i must provide my answer to it because my problem was not solved by others.

first of all : i was dynamically adding fragments using fragmentTransactions. Second: my fragments were modified using AsyncTasks (DB queries on a server). Third: my fragment was not instantiated at activity start Fourth: i used a custom fragment instantiation "create or load it" in order to get the fragment variable. Fourth: activity was recreated because of orientation change

The problem was that i wanted to "remove" the fragment because of the query answer, but the fragment was incorrectly created just before. I don't know why, probably because of the "commit" be done later, the fragment was not added yet when it was time to remove it. Therefore getActivity() was returning null.

Solution : 1)I had to check that i was correctly trying to find the first instance of the fragment before creating a new one 2)I had to put serRetainInstance(true) on that fragment in order to keep it through orientation change (no backstack needed therefore no problem) 3)Instead of "recreating or getting old fragment" just before "remove it", I directly put the fragment at activity start. Instantiating it at activity start instead of "loading" (or instantiating) the fragment variable before removing it prevented getActivity problems.

Solution 8 - Android

In Kotlin you can try this way to handle getActivity() null condition.

   activity?.let { // activity == getActivity() in java

        //your code here

   }

It will check activity is null or not and if not null then execute inner code.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeorgy GobozovView Question on Stackoverflow
Solution 1 - AndroidGeorgy GobozovView Answer on Stackoverflow
Solution 2 - AndroidPawan MaheshwariView Answer on Stackoverflow
Solution 3 - AndroidPaul FreezView Answer on Stackoverflow
Solution 4 - AndroidbvmobileappsView Answer on Stackoverflow
Solution 5 - AndroidMapsyView Answer on Stackoverflow
Solution 6 - AndroidMohanView Answer on Stackoverflow
Solution 7 - AndroidFeubyView Answer on Stackoverflow
Solution 8 - AndroidSachinView Answer on Stackoverflow