为什么getContext()
有时会返回null
?我将上下文传递给LastNewsRVAdapter.java
作为参数。但LayoutInflater.from(context)
有时会崩溃。我在播放控制台上收到了一些崩溃报告。以下是崩溃报告。
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)
这是LastNewsRVAdapter.java
构造函数。
public LastNewsRVAdapter(Context context, List<LatestNewsData>
latestNewsDataList, FirstPageSideBanner sideBanner) {
this.context = context;
this.latestNewsDataList = latestNewsDataList;
inflater = LayoutInflater.from(context);
this.sideBanner = sideBanner;
}
这是Fragment
中的代码onCreateView
@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;
}
这是Fragment
中的refresh
方法
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) {
}
});
答案 0 :(得分:15)
作为已接受的答案,您应该研究您使用和缓存上下文的方式。不知何故,你正在泄漏造成空指针异常的上下文。
以下答案是根据问题的第一个修订版。
使用onAttach()
获取上下文。
// 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;
}
此上下文将在onCreateView中提供,因此您应该使用它。
警告:如果
Context
中需要Fragment
个对象,则可以致电getContext()
。但是,请务必仅致电getContext()
当片段附加到活动时。当片段不是 尚未附着,或在其生命周期结束时被分离,getContext()
将返回null
。
答案 1 :(得分:15)
首先,正如您在此link上看到的那样,片段生命周期内的onCreateView()方法位于onAttach()之后,因此您应该已经有了一个上下文。您可能想知道,为什么getContext()会返回null呢?问题在于您创建适配器的位置:
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) {
}
});
虽然您在onCreateView()中指定了一个回调,但这并不意味着该回调中的代码将在该点运行。它将在网络调用完成后运行并创建适配器。考虑到这一点,您可以在片段的生命周期中的该点之后运行回调。我的意思是用户可以进入该屏幕(片段)并转到其他片段或在网络请求完成之前返回到前一个片段(并且回调运行)。如果发生这种情况,那么如果用户离开片段,则getContext()可以返回null(可能已经调用了onDetach())。
此外,如果活动在网络请求完成之前被销毁,您也可能会发生内存泄漏。所以你有两个问题。
我解决这些问题的解决方案是:
为了避免空指针异常和内存泄漏,你应该在调用片段内的onDestroyView()时取消网络请求(retrofit返回一个可以取消请求的对象:{{3 }})。
另一个阻止空指针异常的选项是在回调之外移动适配器LastNewsRVAdapter的创建,并在片段中保留对它的引用。然后,在回调中使用该引用来更新适配器的内容:link
答案 2 :(得分:1)
因此getContext()
未调用onCreateView()
。它在onResponse()
回调内部调用,可以随时调用。如果在您的片段未附加到活动时调用它,getContext()
将返回null。
这里理想的解决方案是在活动/片段未激活时取消请求(例如用户按下后退按钮,或最小化应用程序)。
另一种解决方案是,当您的片段未附加到您的活动时,忽略onResponse()
中的内容,例如:https://stackoverflow.com/a/10941410/4747587
答案 3 :(得分:1)
当您确定片段已附加到其主机(onResume,onViewCreated等)时,请使用它代替 getContext():
requireContext()
它不会被皮棉检查,但是如果上下文分离,它将抛出异常!
然后,您应该通过 clouse (或其他方法)检查无效性,或者确保如果程序到达此行,则它不为空。
在改造呼叫或改造中(可能还会有其他方式),它将返回一次性,必须在 onDestroy 之前清除或处置。
答案 4 :(得分:0)
编写一个通用方法,确保您永远不会得到 null Context
。
public class BaseFragment extends Fragment {
private Context contextNullSafe;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
/*View creation related to this fragment is finished here. So in case if contextNullSafe is null
* then we can populate it here.In some discussion in - https://stackoverflow.com/questions/6215239/getactivity-returns-null-in-fragment-function
* and https://stackoverflow.com/questions/47987649/why-getcontext-in-fragment-sometimes-returns-null,
* there are some recommendations to call getContext() or getActivity() after onCreateView() and
* onViewCreated() is called after the onCreateView() call every time */
if (contextNullSafe == null) getContextNullSafety();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
contextNullSafe = context;
}
/**CALL THIS IF YOU NEED CONTEXT*/
public Context getContextNullSafety() {
if (getContext() != null) return getContext();
if (getActivity() != null) return getActivity();
if (contextNullSafe != null) return contextNullSafe;
if (getView() != null && getView().getContext() != null) return getView().getContext();
if (requireContext() != null) return requireContext();
if (requireActivity() != null) return requireActivity();
if (requireView() != null && requireView().getContext() != null)
return requireView().getContext();
return null;
}
/**CALL THIS IF YOU NEED ACTIVITY*/
public FragmentActivity getActivityNullSafety() {
if (getContextNullSafety() != null && getContextNullSafety() instanceof FragmentActivity) {
/*It is observed that if context it not null then it will be
* the related host/container activity always*/
return (FragmentActivity) getContextNullSafety();
}
return null;
}