getActivity () renvoie null dans la fonction Fragment

192

J'ai un fragment (F1) avec une méthode publique comme celle-ci

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

et oui quand je l'appelle (à partir de l'activité), c'est nul ...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

Ce doit être quelque chose que je fais très mal, mais je ne sais pas ce que c'est

Lukap
la source
Je ne sais pas s'il y a eu juste une erreur lorsque vous l'avez collé dans cet article, mais vous avez besoin de parenthèses après getActivity(). De plus, comment instanciez-vous le fragment? L'avez-vous dans votre layout.xml?
CaseyB
À quoi appartient le deuxième fragment de code? À la oncreate () - méthode de l'activité? Et avez-vous déjà appelé setContentView ()?
Franziskus Karsunke
R.id.upperPar est un élément de la mise en page, donc il est censé être remplacé par le fragment, mais ce n'est pas mon problème. Je ne comprends pas pourquoi j'obtiens null lorsque j'appelle getActivity () dans des méthodes de fragment personnalisées, disons dans la méthode onActivityCreated getActivity est l'activité réelle non nulle
Lukap
le problème ce n'est pas dans les mises en page, l'application fonctionne bien mais pourquoi j'obtiens null pour le getActivity?, btw tous les éléments, y compris le fragment, il est rendu comme il ne devrait pas
poser de
1
Vous devez appeler cette méthode: f1.asd (); dans la méthode onActivityCreated qui doit être remplacée dans votre classe de fragment.
Namrata Bagerwal

Réponses:

164

commit planifie la transaction, c'est-à-dire qu'elle ne se produit pas tout de suite mais est planifiée comme un travail sur le thread principal la prochaine fois que le thread principal est prêt.

Je suggère d'ajouter un

onAttach(Activity activity)

méthode à votre Fragmentet en mettant un point d'arrêt dessus et en voyant quand il est appelé par rapport à votre appel asd(). Vous verrez qu'il est appelé après la méthode où vous faites l'appel aux asd()sorties. L' onAttachappel est l'endroit où le Fragmentest attaché à son activité et à partir de ce point getActivity()retournera non nul (nb il y a aussi un onDetach()appel).

PJL
la source
5
Je n'ai pas compris comment vous pouvez résoudre votre problème. Si mon getActivity () n'est pas encore prêt, comment puis-je obtenir la référence de l'objet FragmentActivity?
CeccoCQ
2
@Vivek Je ne sais pas trop ce que vous voulez réaliser. Si vous avez besoin que le Fragment affiche immédiatement une boîte de dialogue, demandez-lui de faire ce qu'il doit faire lors de la création, par exemple dans ses méthodes onCreateViewou onActivityCreated. Je me demande pourquoi asd () doit être appelé quand il le fait dans la publication des questions.
PJL
3
onAttach obsolète
abbasalim
6
onAttach (Activity mActivity) semble être déprécié .. toute solution de contournement pour cela
ashish.n
4
API 24 introduitcommitNow()
Nicolas
92

Le mieux pour s'en débarrasser est de conserver la référence d'activité lorsque onAttach est appelé et d'utiliser la référence d'activité là où c'est nécessaire, par exemple

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
Pawan Maheshwari
la source
34
Devrions-nous définir mActivity = null onDetach ()?
Oliver Pearmain
5
@OliverPearmain si vous le faites dans onDetach () alors il n'y aura aucun profit. Vous devez l'annuler dans onDestory (). De plus, vous devez le conserver dans WeakRefernce.
Kirill Popov
Je l'annule dans les deux onDestroy()et onDetach()parce qu'il onDestroy()n'est pas garanti d'être appelé.
Mohammed Ali
8
Sommes-nous en train de divulguer le Activitysi nous ne l'annulons pas onDestroy()?
Mohammed Ali
3
Selon developer.android.com/intl/zh-tw/guide/components/… , onAttach () est appelé avant d'appeler onCreateView (). Mais j'obtiens toujours une NullPointerException pendant que j'appelle getActivity () dans onCreateView (). Comment cela pourrait-il arriver?
Kimi Chiu
82

Cela se produit lorsque vous appelez getActivity()un autre thread qui s'est terminé après la suppression du fragment. Le cas typique est l'appel getActivity()(par exemple pour a Toast) lorsqu'une requête HTTP est terminée ( onResponsepar exemple).

Pour éviter cela, vous pouvez définir un nom de champ mActivityet l'utiliser à la place de getActivity(). Ce champ peut être initialisé dans la méthode onAttach () de Fragment comme suit:

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

Dans mes projets, je définis généralement une classe de base pour tous mes fragments avec cette fonctionnalité:

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

Bon codage,

Thucnguyen
la source
19
devons-nous mettre mActivity = null; dans onDetach ()?
Bharat Dodeja
thucnguyen, que diriez-vous de déclarer mActivity statique pour une application d'activité unique?
iamthevoid
Je ne vois pas du tout de raison d'accéder à Activitypartir d'un autre fil. Vous ne pouvez rien faire avec ça de toute façon, même pas montrer un toast. Vous devez donc soit transférer d'abord le travail vers le thread principal, soit ne pas utiliser d'activité du tout.
Dzmitry Lazerka
4
@BharatDodeja devrions-nous définir mActivity = null onDetach? Avez-vous découvert?
SLearner
5
Cela va fuir sans annuler l'activité.
Darek Deoniziak
30

Les autres réponses qui suggèrent de garder une référence à l'activité dans onAttach suggèrent simplement un pansement au problème réel. Lorsque getActivity renvoie null, cela signifie que le fragment n'est pas attaché à l'activité. Le plus souvent, cela se produit lorsque l'activité a disparu en raison de la rotation ou de la fin de l'activité, mais que le fragment a toujours une sorte d'écouteur de rappel enregistré. Lorsque l'auditeur est appelé, si vous devez faire quelque chose avec l'activité mais que l'activité a disparu, vous ne pouvez pas faire grand-chose. Dans votre code, vous devez simplement vérifiergetActivity() != nullet si ce n'est pas là, ne faites rien. Si vous conservez une référence à l'activité qui a disparu, vous empêchez l'activité d'être récupérée. Toutes les actions de l'interface utilisateur que vous pourriez essayer de faire ne seront pas vues par l'utilisateur. Je peux imaginer des situations où, dans l'écouteur de rappel, vous voudrez peut-être avoir un contexte pour quelque chose de non lié à l'interface utilisateur, dans ces cas, il est probablement plus logique d'obtenir le contexte de l'application. Notez que la seule raison pour laquelle l' onAttachastuce n'est pas une grosse fuite de mémoire est que normalement après l'exécution de l'écouteur de rappel, il ne sera plus nécessaire et peut être récupéré avec le fragment, toutes ses vues et le contexte d'activité. Si voussetRetainInstance(true) il y a un plus grand risque de fuite de mémoire car le champ Activité sera également conservé mais après la rotation, cela pourrait être l'activité précédente et non l'actuelle.

miguel
la source
1
C'est exactement mon problème. J'ai un fragment qui fait un processus -> puis une annonce est affichée -> et puis le processus continue. Sur certains appareils, après le retour de l'annonce (via un écouteur des événements publicitaires), getActivity () est nul. Mais je dois continuer à faire l'autre partie du travail pour terminer le travail. Voulez-vous dire qu'il n'y a pas de solution à cela?
Notbad
C'est exactement ce à quoi je suis confronté. J'ai une interface d'activité dans un fragment où je fais certaines choses de facturation. Une fois le paiement effectué, je souhaite utiliser l'interface pour faire quelque chose, mais l'interface est nulle.
Freddie
Cela semble être la bonne réponse générale à des centaines de questions SO sur ce sujet.
Manuel
Meilleure réponse. Il y a tellement de solutions bandaid pour Android sur SO.
maxbeaudoin
Donc, si je veux faire une opération, comment puis-je l'exécuter après que getActivity () soit disponible (le cas échéant).
Sreekanth Karumanaghat
17

Depuis le niveau 23 d'API Android, onAttach (activité d'activité) est obsolète. Vous devez utiliser onAttach (contexte de contexte). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

L'activité est un contexte, donc si vous pouvez simplement vérifier le contexte est une activité et la cast si nécessaire.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}
Sachin
la source
Comment l'utiliser
ARR.s
10

PJL a raison. J'ai utilisé sa suggestion et voici ce que j'ai fait:

  1. variables globales définies pour le fragment:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. mis en œuvre

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3. J'ai terminé ma fonction, où j'ai besoin d'appeler getActivity (), dans thread, car si elle s'exécutait sur le thread principal, je bloquerais le thread avec l'étape 4. et onAttach () ne serait jamais appelé.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4. dans ma fonction où j'ai besoin d'appeler getActivity (), je l'utilise (avant l'appel getActivity ())

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Si vous avez des mises à jour de l'interface utilisateur, n'oubliez pas de les exécuter sur le fil de l'interface utilisateur. J'ai besoin de mettre à jour ImgeView donc j'ai fait:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});
zajac.m2
la source
7

L'ordre dans lequel les rappels sont appelés après commit ():

  1. Quelle que soit la méthode que vous appelez manuellement juste après commit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

J'avais besoin de faire un travail qui impliquait des vues, donc onAttach () ne fonctionnait pas pour moi; il s'est écrasé. J'ai donc déplacé une partie de mon code qui définissait certains paramètres dans une méthode appelée juste après commit () (1.), puis l'autre partie du code qui gérait la vue à l'intérieur de onCreateView () (3.).

Bogdan Zurac
la source
3

J'utilise OkHttp et je viens de faire face à ce problème.


Pour la première partie, @thucnguyen était sur la bonne voie .

Cela s'est produit lorsque vous appelez getActivity () dans un autre thread qui s'est terminé après la suppression du fragment. Le cas typique est l'appel de getActivity () (par exemple pour un Toast) lorsqu'une requête HTTP est terminée (dans onResponse par exemple).

Certains appels HTTP étaient en cours d'exécution même après la fermeture de l'activité (car cela peut prendre un certain temps pour qu'une requête HTTP soit terminée). J'ai ensuite HttpCallbackessayé de mettre à jour certains champs Fragment et j'ai obtenu une nullexception en essayant de le faire getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO, la solution est d' empêcher les rappels de se produire lorsque le fragment n'est plus en vie (et ce n'est pas seulement avec Okhttp).

Le correctif: la prévention.

Si vous regardez le cycle de vie des fragments (plus d'informations ici ), vous remarquerez qu'il existe des méthodes onAttach(Context context)et onDetach(). Ceux-ci sont appelés après que le fragment appartient à une activité et juste avant de cesser de l'être respectivement.

Cela signifie que nous pouvons empêcher ce rappel de se produire en le contrôlant dans la onDetachméthode.

@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();
    }
}
zurfyx
la source
2

Où appelez-vous cette fonction? Si vous l'appelez dans le constructeur de Fragment, il retournera null.

Appelez simplement getActivity()lorsque la méthode onCreateView()est exécutée.

Phạm Lam
la source
1

Faites comme suit. Je pense que cela vous sera utile.

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

}
Vinil Chandran
la source
1

Ceux qui ont encore le problème avec onAttach (activité d'activité), il vient de changer en contexte -

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

Dans la plupart des cas, la sauvegarde du contexte vous suffira - par exemple, si vous voulez faire getResources (), vous pouvez le faire directement à partir du contexte. Si vous devez encore intégrer le contexte à votre activité, faites-le -

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

Comme suggéré par user1868713.

Shai
la source
0

Vous pouvez utiliser onAttach ou si vous ne voulez pas mettre onAttach partout, vous pouvez mettre une méthode qui retourne ApplicationContext sur la classe App principale:

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

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

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

Après cela, vous pouvez le réutiliser partout dans tout votre projet, comme ceci:

App.getContext().getString(id)

S'il vous plaît laissez-moi savoir si cela ne fonctionne pas pour vous.

surga
la source
0

Une autre bonne solution serait d'utiliser LiveData d'Android avec l'architecture MVVM. Vous définiriez un objet LiveData dans votre ViewModel et l'observeriez dans votre fragment, et lorsque la valeur LiveData est modifiée, il avertirait votre observateur (fragment dans ce cas) uniquement si votre fragment est à l'état actif, il serait donc garanti que vous ferait fonctionner votre interface utilisateur et accéder à l'activité uniquement lorsque votre fragment est à l'état actif. C'est l'un des avantages de LiveData

Bien sûr, lorsque cette question a été posée pour la première fois, il n'y avait pas de LiveData. Je laisse cette réponse ici parce que, comme je le vois, il y a toujours ce problème et cela pourrait être utile à quelqu'un.

Serdar Samancıoğlu
la source
0

Appelez la méthode getActivity () dans onActivityCreated ()

MuM6oJuM6o
la source