Android. Fragment getActivity() sometimes returns null
Solution 1
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
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
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
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
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 Runnable
s within a valid Activity
context whenever there's a valid reference available. The Runnable
s 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.
}
}
);
Related videos on Youtube
Georgy Gobozov
Updated on July 14, 2021Comments
-
Georgy Gobozov almost 3 years
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?
-
Georgy Gobozov almost 12 yearsI figured out a problem, but not solution. I don't know why but fragment resume earlier activity. And this only happens when my app at last position in list recently apps, it seems system destroys my application.
-
Georgy Gobozov almost 12 yearsWhen I resume my application from background fragmetn onCreate an onResume called before activity onCreate/onResume method. It seem some detached fragment still alive and trying to resume.
-
Georgy Gobozov almost 12 yearsin this string firstTask.setTaskListener((TaskListener) adapter.getItem(0)); adapter.getItem(0) return old fragment, adapter do not remove fragments correctrly
-
Prizoff over 11 yearsGreat activity by the way :) question asked, comments left and answer given - all are done by a single person! +1 for these.
-
sha over 8 yearssave the Context( getActivity() ) in onCreateView() as this this is called when view is recreated in background case.
-
-
Pawan Maheshwari over 10 yearsFragments always keeps its parent activity's reference and makes you available with getActivity() method, here we are keeping the same reference.
-
Vering over 10 yearsGoogle actually recommends this if you need your fragment to share events with the activity. developer.android.com/guide/components/fragments.html (look for "Creating event callbacks to the activity")
-
User about 10 yearsTHis is annoying though, having to call
isAdded()
before each access... makes code ugly. -
midnight about 10 yearsyou might want to add onDetach method, which nullifies the activity reference
-
Pawan Maheshwari about 10 yearsyeah initialize mActivity=null on onDetach method to nullify that activity reference.
-
r1k0 about 10 yearsI think this actually works because of the fact that the fragment is added to the activity when the onAttach method is called. See the update in the answer below.
-
Snicolas almost 10 yearsTo get some good memory prevention leak, a WeakReference could be used, with the couple onAttach/onDetach. But is that really different from getActivity() ?
-
CoDe almost 10 yearswhat if u want to access activity object in fragment onCreateView itself...then this solution will not work....any other suggestion.
-
Sufian over 9 years@Vering well it didn't suggest to write
mActivity = activity;
. It merely saidmListener = (OnArticleSelectedListener) activity;
which is fine but not an answer to the original question. -
Lo-Tan over 9 years@Shubh onAttach(Activity) is invoked prior to onCreateView in the fragment lifecycle. It's actually the very first invocation. Why won't this work for you?
-
njzk2 over 9 yearsnever ever do that. you are leaking your complete activity (and with it the whole layout tree, with drawables and such). If
getActivity()
returns null, it is because you are not in an activity anymore. This is a dirty workaround. -
Bevor about 9 yearsI do this within my ctor when creating the fragment, so I get no NPE. And it isn't a leak when you set the reference to null when closing the fragment.
-
StackOverflowed almost 9 yearsThere doesn't seem to be much of a difference between having
if(isAdded())
orif(getActivity() != null)
-
Supreme Dolphin almost 8 yearsActually, I had a similar problem because I was starting the task in the the fragment constructor. Thanks a lot.
-
abarisone almost 8 yearsCould you please elaborate more your answer adding a little more description about the solution you provide?
-
Hadas Kaminsky almost 8 yearsGreat answer! should be marked as the correct one since it solves the real problem: In my case it's not enough to check that getActivity() is not null because I must complete my task no matter what. Am using this and it works perfectly.
-
Knarf over 7 yearsI personnally did this in my app, and came to the conclusion that this seems to be an improvement over just calling getActiivty(), but sometimes this will also return null.
-
Pawan Maheshwari over 7 years@njzk2 - you can set activity=null while deAttach of fragment.
-
Pawan Maheshwari over 7 years@njzk2 - Usually this is needed when you are in transition state while your callback is returning. Either you write observables & detach callback while activity stop/destroy or keep reference to avoid crashes. Obviously first one is recommended approach
-
fahad_sust about 5 yearsCan you add an example of using this.getActivityBuffer().safely(...) method.
-
Sreekanth Karumanaghat over 4 years@njzk2 Please advice on how to solve this problem. I want to do some operation like commitFragmentTransaction() on the activity, how can I do this?