java.lang.IllegalStateException: Fragment non attaché à l'activité

148

J'obtiens rarement cette erreur lors d'un appel API.

java.lang.IllegalStateException: Fragment  not attached to Activity

J'ai essayé de mettre le code à l'intérieur de la isAdded()méthode pour vérifier si le fragment est actuellement ajouté à son activité, mais j'obtiens toujours rarement cette erreur. Je ne comprends pas pourquoi j'obtiens toujours cette erreur. Comment puis-je l'empêcher?

Son erreur d'affichage sur la ligne-

cameraInfo.setId(getResources().getString(R.string.camera_id));

Vous trouverez ci-dessous l'exemple d'appel d'API que je fais.

SAPI.getInfo(getActivity(),
                new APIResponseListener() {
                    @Override
                    public void onResponse(Object response) {


                        cameraInfo = new SInfo();
                        if(isAdded()) {
                            cameraInfo.setId(getResources().getString(R.string.camera_id));
                            cameraInfo.setName(getResources().getString(R.string.camera_name));
                            cameraInfo.setColor(getResources().getString(R.string.camera_color));
                            cameraInfo.setEnabled(true);
                        }


                    }

                    @Override
                    public void onError(VolleyError error) {
                        mProgressDialog.setVisibility(View.GONE);
                        if (error instanceof NoConnectionError) {
                            String errormsg = getResources().getString(R.string.no_internet_error_msg);
                            Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                        }
                    }
                });
Développeur Android
la source
cameraInfo.setId (getActivity (). getResources (). getString (R.string.camera_id));
Ashwin H

Réponses:

203

Cette erreur se produit en raison de l'effet combiné de deux facteurs:

  • La requête HTTP, une fois terminée, appelle soit onResponse()ou onError()(qui fonctionne sur le thread principal) sans savoir si le Activityest toujours au premier plan ou non. Si le Activityest parti (l'utilisateur a navigué ailleurs), getActivity()renvoie null.
  • Le Volley Responseest exprimé comme une classe interne anonyme, qui contient implicitement une référence forte à la Activityclasse externe . Cela entraîne une fuite de mémoire classique.

Pour résoudre ce problème, vous devez toujours faire:

Activity activity = getActivity();
if(activity != null){

    // etc ...

}

et aussi, utilisez isAdded()dans la onError()méthode:

@Override
public void onError(VolleyError error) {

    Activity activity = getActivity(); 
    if(activity != null && isAdded())
        mProgressDialog.setVisibility(View.GONE);
        if (error instanceof NoConnectionError) {
           String errormsg = getResources().getString(R.string.no_internet_error_msg);
           Toast.makeText(activity, errormsg, Toast.LENGTH_LONG).show();
        }
    }
}
YS
la source
2
Lorsque vous utilisez des requêtes Volley et AsyncTasks depuis un Activity, il n'existe aucun moyen infaillible d'éviter les NPE. Il y a toujours une chance que l'utilisateur s'éloigne du courant Activitypendant que l'un des threads fait quelque chose en arrière-plan, puis lorsque le thread se termine et onPostExecute()ou onResponse()est appelé, il n'y a pas Activity. Tout ce que vous pouvez faire est de vérifier les références nulles à divers points de votre code, et ce n'est pas à l'épreuve des balles :)
YS
2
Le test de singe Android (adb shell monkey) est vraiment bon pour éliminer cette erreur si vous ne l'avez pas déjà prise en compte de manière générique / globale.
Groovee60
5
isAdded () suffit, le booléen public final isAdded () {return mActivity! = null && mAdded; }
lannyf
2
@ruselli: vérifie l' addedindicateur booléen et si l' Activityinstance actuelle l' est nullou non.
YS
1
@gauravjain Évitez de faire des requêtes asynchrones (comme des appels HTTP) directement à partir de Fragments. Faites-le à partir de l'activité et ça devrait aller. En outre, effacez les références Fragment du FragmentManager, c'est une bonne pratique et c'est le meilleur moyen d'éviter les fuites de mémoire.
YS
56

Le cycle de vie des fragments est très complexe et plein de bugs, essayez d'ajouter:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}
Miroslav Michalec
la source
2
Où dois-je le mettre?
Vaclovas Rekašius Jr.
1
@ VaclovasRekašiusJr. On dirait à peu près partout où vous souhaitez accéder à l'activité depuis l'intérieur du fragment. Amusement!
TylerJames
2
que dois-je faire si activité == null. pour garder en vie mon application @Miroslav
pavel
Regardez dans isAdded () , vous pourriez trouver "activité! = Null" n'est pas redondant
BertKing
@BertKing return mHost != null && mAdded;- C'est ce que contient la méthode fragment.isAdded (). Je pensais que mHost est une activité si vous la tracez, mais il semble que mHost soit à l'intérieur de FragmentActivity. Alors, probablement, vous avez raison. Des ajouts?
Johnny Five le
14

I Found Very Simple Solution isAdded () méthode qui est l'une des méthodes de fragment pour identifier que ce fragment actuel est attaché à son activité ou non.

nous pouvons utiliser ceci comme partout dans la classe de fragment comme:

if(isAdded())
{

// using this method, we can do whatever we want which will prevent   **java.lang.IllegalStateException: Fragment not attached to Activity** exception.

}
Dharmesh Baldha
la source
12

Exception: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} non associé à l'activité

Catégorie: Cycle de vie

Description : Lors d'une opération fastidieuse dans le thread d'arrière-plan (par exemple, AsyncTask), un nouveau fragment a été créé entre-temps et a été détaché de l'activité avant la fin du thread d'arrière-plan. Le code dans le thread d'interface utilisateur (par exemple, onPostExecute) fait appel à un fragment détaché, lançant une telle exception.

Solution fixe:

  1. Annuler le fil d'arrière-plan lors de la mise en pause ou de l'arrêt du fragment

  2. Utilisez isAdded () pour vérifier si le fragment est attaché, puis pour getResources () à partir de l'activité.

Rahil Ali
la source
11

Je peux être en retard mais peut aider quelqu'un ..... La meilleure solution pour cela est de créer une instance de classe d'application globale et de l'appeler dans le fragment particulier où votre activité n'est pas attachée

comme ci-dessous

icon = MyApplication.getInstance().getString(R.string.weather_thunder);

Voici la classe d'application

public class MyApplication extends Application {

    private static MyApplication mInstance;
    private RequestQueue mRequestQueue;

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

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
}
md gouse
la source
1
Oui, cette méthode est également largement utilisée.
CoolMind
1
Ce n'est pas une option judicieuse. FragmentContext et ApplicationContext ont des styles différents. Le contexte de fragment peut avoir un thème sombre, un style personnalisé, des paramètres régionaux, etc. qui tireront la couleur, les ressources de chaîne de différents fichiers. Alors que ApplicationContext peut ne pas extraire la ressource correcte. Si vous n'avez pas de contexte, vous ne devriez pas essayer de rendre cette ressource.
Jemshit Iskenderov
2

Cette erreur peut se produire si vous instanciez un fragment qui ne peut pas être instancié d'une manière ou d'une autre:

Fragment myFragment = MyFragment.NewInstance();


public classs MyFragment extends Fragment {
  public void onCreate() {
   // Some error here, or anywhere inside the class is preventing it from being instantiated
  }
}

Dans mon cas, j'ai rencontré ceci lorsque j'ai essayé d'utiliser:

private String loading = getString(R.string.loading);
s'affaisse
la source
2

En utilisation Fragment isAdded() Il retournera vrai si le fragment est actuellement attaché à Activity.

Si vous souhaitez vérifier dans l'activité

 Fragment fragment = new MyFragment();
   if(fragment.getActivity()!=null)
      { // your code here}
      else{
       //do something
       }

J'espère que cela aidera quelqu'un

Prateek218
la source
1

J'ai adopté l'approche suivante pour traiter ce problème. Création d'une nouvelle classe qui agit comme un wrapper pour des méthodes d'activité comme celle-ci

public class ContextWrapper {
    public static String getString(Activity activity, int resourceId, String defaultValue) {
        if (activity != null) {
            return activity.getString(resourceId);
        } else {
            return defaultValue;
        }
    }

    //similar methods like getDrawable(), getResources() etc

}

Maintenant, partout où j'ai besoin d'accéder à des ressources à partir de fragments ou d'activités, au lieu d'appeler directement la méthode, j'utilise cette classe. Dans le cas où l'activité contextn'est pas, nullil renvoie la valeur de l'actif et dans le cas oùcontext est nulle, elle transmet une valeur par défaut (qui est également spécifiée par l'appelant de la fonction).

Important Ce n'est pas une solution, c'est un moyen efficace de gérer ce plantage avec élégance. Vous voudrez ajouter des journaux dans les cas où vous obtenez une instance d'activité comme nulle et essayez de résoudre ce problème, si possible.

Ezio
la source
0

cela se produit lorsque le fragment n'a pas de contexte, donc la méthode getActivity () renvoie null. vérifiez si vous utilisez le contexte avant de l'obtenir , ou si l'activité n'existe plus. utilisez le contexte dans fragment.onCreate et après la réponse de l'API, ce problème est généralement le cas

fan dexian
la source
0

Parfois, cette exception est causée par un bogue dans l'implémentation de la bibliothèque de support. Récemment, j'ai dû passer de 26.1.0 à 25.4.0 pour m'en débarrasser.

Bord81
la source
Non, je ne sais pas, mais je devrais peut-être en créer un.
Bord81
0

Ce problème se produit chaque fois que vous appelez un contexte qui n'est pas disponible ou nul lorsque vous l'appelez. Cela peut être une situation lorsque vous appelez le contexte du thread d'activité principal sur un thread d'arrière-plan ou le contexte du thread d'arrière-plan sur le thread d'activité principal.

Par exemple, j'ai mis à jour ma chaîne de préférence partagée comme suit.

editor.putString("penname",penNameEditeText.getText().toString());
editor.commit();
finish();

Et appelé finish () juste après. Maintenant, ce qu'il fait, c'est que la validation s'exécute sur le thread principal et arrête toute autre validation Async si elle arrive jusqu'à ce qu'elle se termine. Son contexte est donc vivant jusqu'à ce que l'écriture soit terminée. Par conséquent, le contexte précédent est en direct, ce qui provoque l'erreur.

Assurez-vous donc de faire revérifier votre code s'il y a du code ayant ce problème de contexte.

Prashant Paliwal
la source
comment avez-vous réussi à résoudre ce problème? Je l'appelle dans un thread asynchrone et je rencontre ce problème maintenant.
Simon
Assurez-vous simplement que l'opération d'écriture est terminée, puis seul le contexte est tué pas avant la fin de l'opération d'écriture.
Prashant Paliwal