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 14:10

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

}
查看更多
还给你的自由
3楼-- · 2018-12-31 14:11

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();
    }
}
查看更多
春风洒进眼中
4楼-- · 2018-12-31 14:11

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.

查看更多
其实,你不懂
5楼-- · 2018-12-31 14:13

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.

查看更多
听够珍惜
6楼-- · 2018-12-31 14:13

You can using onAttach or if you do not want to put onAttach everywhere then you can put a method that returns ApplicationContext on the main App class :

public class App {
    ...  
    private static Context context;

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

    public static Context getContext() {
        return context;
    }
    ...
}

After that you can re-use it everywhere in all over your project, like this :

App.getContext().getString(id)

Please let me know if this does not work for you.

查看更多
怪性笑人.
7楼-- · 2018-12-31 14:16

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

查看更多
登录 后发表回答