getActivity() returns null in Fragment function

2018-12-31 13:35发布

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

12条回答
大哥的爱人
2楼-- · 2018-12-31 13: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 has some kind of callback listener. 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 might 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.

查看更多
几人难应
3楼-- · 2018-12-31 13:56

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;
}
查看更多
荒废的爱情
4楼-- · 2018-12-31 13:57

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(Activity activity) {
super.onAttach(activity);
    mActivity = activity;
}

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(Activity activity) {
        super.onAttach(activity);
        mActivity = (FragmentActivity) activity;
    }
}

Happy coding,

查看更多
春风洒进眼中
5楼-- · 2018-12-31 13:59

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);
    }
});
查看更多
流年柔荑漫光年
6楼-- · 2018-12-31 14:00

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

查看更多
素衣白纱
7楼-- · 2018-12-31 14:06

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

}
查看更多
登录 后发表回答