web-dev-qa-db-fra.com

Pourquoi getContext () dans le fragment renvoie parfois null?

Pourquoi getContext() renvoie parfois null? Je passe le contexte à LastNewsRVAdapter.Java en tant qu'argument. Mais LayoutInflater.from(context) plante parfois. Je reçois quelques rapports de plantage sur la console de jeu. Ci-dessous le rapport de crash.

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)

Ceci est le constructeur LastNewsRVAdapter.Java.

public LastNewsRVAdapter(Context context, List<LatestNewsData> 
    latestNewsDataList, FirstPageSideBanner sideBanner) {
    this.context = context;
    this.latestNewsDataList = latestNewsDataList;
    inflater = LayoutInflater.from(context);
    this.sideBanner = sideBanner;
}

Ceci est le code onCreateView à l'intérieur du 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;
}

Ceci est la méthode refresh à l'intérieur de 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) {

        }
    });
17
Bek

Tout d'abord, comme vous pouvez le voir sur ce lien link , la méthode onCreateView () dans le cycle de vie du fragment vient après onAttach (), vous devriez donc déjà avoir un contexte à cet endroit. Vous vous demandez peut-être pourquoi getContext () renvoie alors null? le problème réside à l'endroit où vous créez votre adaptateur:

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

        }
    });

Bien que vous spécifiiez un rappel dans onCreateView (), cela ne signifie pas que le code à l'intérieur de ce rappel sera exécuté à ce stade. Il s'exécutera et créera l'adaptateur une fois l'appel réseau terminé. En gardant cela à l'esprit, votre rappel peut s'exécuter après ce stade du cycle de vie du fragment. Ce que je veux dire, c’est que l’utilisateur peut entrer dans cet écran (fragment) et accéder à un autre fragment ou revenir au précédent avant la fin de la demande réseau (et le rappel exécuté). Si cela se produit, alors getContext () peut renvoyer null si l'utilisateur quitte le fragment (onDetach () a peut-être été appelé).

En outre, vous pouvez également avoir des fuites de mémoire si l’activité est détruite avant la fin de votre demande de réseau. Donc, vous avez deux problèmes ici.

Mes solutions pour résoudre ces problèmes sont les suivantes:

  1. afin d'éviter l'exception de pointeur nul et la fuite de mémoire, vous devez annuler la demande réseau lorsque la fonction onDestroyView () du fragment est appelée (la modification retourne un objet pouvant annuler la demande: link ).

  2. Une autre option permettant d'éviter l'exception de pointeur null consiste à déplacer la création de l'adaptateur LastNewsRVAdapter en dehors du rappel et à conserver une référence à celui-ci dans le fragment. Ensuite, utilisez cette référence dans le rappel pour mettre à jour le contenu de l'adaptateur: link

13
Leandro Ocampo

En tant que réponse acceptée, vous devez examiner votre manière d’utiliser et de mettre en cache le contexte. D'une manière ou d'une autre, vous perdez le contexte qui cause l'exception Null Pointer Exception.


La réponse ci-dessous correspond à la première révision de la question.

Utilisez onAttach() pour obtenir le contexte.

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

Ce contexte sera disponible dans onCreateView, vous devriez donc l'utiliser.


De Fragment Documentation

Attention: Si vous avez besoin d'un objet Context dans votre Fragment, vous pouvez appeler getContext(). Veillez toutefois à appeler getContext() uniquement quand le fragment est attaché à une activité. Quand le fragment n'est pas encore attaché, ou a été détaché à la fin de son cycle de vie, getContext() retournera null.

13
Pankaj Kumar

Donc, getContext() n'est pas appelé dans onCreateView(). Il est appelé dans le callback onResponse() qui peut être appelé à tout moment. Si elle est appelée alors que votre fragment n'est pas associé à une activité, getContext() renverra la valeur null.

La solution idéale consiste ici à annuler la demande lorsque l'activité/le fragment n'est pas actif (comme le bouton enfoncé par l'utilisateur ou la réduction de l'application).

Une autre solution consiste simplement à ignorer tout ce qui est dans onResponse() lorsque votre fragment n'est pas lié à votre activité, comme ceci: https://stackoverflow.com/a/10941410/4747587

1
Henry