问题
Why getContext()
sometimes returns null
? I pass context to LastNewsRVAdapter.java
as an argument. But LayoutInflater.from(context)
sometimes crashes. I'm getting a few crash reports on play console. Below is crash report.
java.lang.NullPointerException
com.example.adapters.LastNewsRVAdapter.<init>
java.lang.NullPointerException:
at android.view.LayoutInflater.from (LayoutInflater.java:211)
at com.example.adapters.LastNewsRVAdapter.<init> (LastNewsRVAdapter.java)
at com.example.fragments.MainFragment$2.onFailure (MainFragment.java)
or .onResponse (MainFragment.java)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run
(ExecutorCallAdapterFactory.java)
at android.os.Handler.handleCallback (Handler.java:808)
at android.os.Handler.dispatchMessage (Handler.java:103)
at android.os.Looper.loop (Looper.java:193)
at android.app.ActivityThread.main (ActivityThread.java:5299)
at java.lang.reflect.Method.invokeNative (Method.java)
at java.lang.reflect.Method.invoke (Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:641)
at dalvik.system.NativeStart.main (NativeStart.java)
This is LastNewsRVAdapter.java
constructor.
public LastNewsRVAdapter(Context context, List<LatestNewsData>
latestNewsDataList, FirstPageSideBanner sideBanner) {
this.context = context;
this.latestNewsDataList = latestNewsDataList;
inflater = LayoutInflater.from(context);
this.sideBanner = sideBanner;
}
This is the code onCreateView
inside Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_main_sonku_kabar, container, false);
tvSonkuKabar = view.findViewById(R.id.textview_sonku_kabar_main);
tvNegizgiKabar = view.findViewById(R.id.textview_negizgi_kabar_main);
refresh(view);
final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.mainRefreshSonkuKabar);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refresh(view);
swipeRefreshLayout.setRefreshing(false);
}
});
setHasOptionsMenu(true);
return view;
}
This is refresh
method inside Fragment
private void refresh(final View view) {
sideBanner = new FirstPageSideBanner();
final RecyclerView rvLatestNews = (RecyclerView) view.findViewById(R.id.recViewLastNews);
rvLatestNews.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
rvLatestNews.setNestedScrollingEnabled(false);
App.getApiService().getLatestNews().enqueue(new Callback<LatestNews>() {
@Override
public void onResponse(Call<LatestNews> call, Response<LatestNews> response) {
if (response.isSuccessful() && response.body().isSuccessfull()){
adapter = new LastNewsRVAdapter(getContext(), response.body().getData(), sideBanner);
rvLatestNews.setAdapter(adapter);
tvSonkuKabar.setVisibility(View.VISIBLE);
}
}
@Override
public void onFailure(Call<LatestNews> call, Throwable t) {
}
});
回答1:
First of all, as you can see on this link, the method onCreateView() inside the fragment's lifecycle comes after onAttach(), so you should have already a context at that point. You may wonder, why does getContext() return null then? the problem lies on where you are creating your adapter:
App.getApiService().getLatestNews().enqueue(new Callback<LatestNews>() {
@Override
public void onResponse(Call<LatestNews> call, Response<LatestNews> response) {
if (response.isSuccessful() && response.body().isSuccessfull()){
adapter = new LastNewsRVAdapter(getContext(), response.body().getData(), sideBanner);
rvLatestNews.setAdapter(adapter);
tvSonkuKabar.setVisibility(View.VISIBLE);
}
}
@Override
public void onFailure(Call<LatestNews> call, Throwable t) {
}
});
Though you are specifying a callback in onCreateView(), that does not mean the code inside that callback will run at that point. It will run and create the adapter after the network call is done. With that in mind, you callback may run after that point in the lifecycle of the fragment. What I mean is that the user can enter to that screen (fragment) and go to other fragment or return to the previous one before the network request finishes (and the callback runs). If that happens, then getContext() could return null if the user leaves the fragment (onDetach() may have been called).
Besides, you can have memory leaks also, in case the activity is destroyed before your network request finishes. So you have two issues there.
My suggestions to solve those issues are:
in order to avoid the null pointer exception and the memory leak, you should cancel the network request when the onDestroyView() inside the fragment is being called (retrofit returns an object that can cancel the request: link).
Another option that will prevent the null pointer exception is to move the creation of the adapter LastNewsRVAdapter outside the callback and keep a reference to it in the fragment. Then, use that reference inside the callback to update the content of the adapter: link
回答2:
As accepted answer, you should look into the way you are using and caching context. Somehow you are leaking context that is causing Null Pointer Exception.
Below Answer is as per first revision of question.
Use onAttach()
to get Context.
// Declare Context variable at class level in Fragment
private Context mContext;
// Initialise it from onAttach()
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
This context will be available in onCreateView, so You should use it.
From Fragment Documentation
Caution: If you need a
Context
object within yourFragment
, you can callgetContext()
. However, be careful to callgetContext()
only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle,getContext()
will returnnull
.
回答3:
So getContext()
is not called in onCreateView()
. It's called inside the onResponse()
callback which can be invoked anytime. If it's invoked when your fragment is not attached to activity, getContext()
will return null.
The ideal solution here is to Cancel the request when the activity/fragment is not active (like user pressed back button, or minimized the app).
Another solution is to simply ignore whatever is in onResponse()
when your fragment is not attached to your activity, like this: https://stackoverflow.com/a/10941410/4747587
来源:https://stackoverflow.com/questions/47987649/why-getcontext-in-fragment-sometimes-returns-null