java.lang.IllegalStateException: Fragment nicht an Aktivität angehängt

147

Ich erhalte diesen Fehler selten, wenn ich einen API-Aufruf mache.

java.lang.IllegalStateException: Fragment  not attached to Activity

Ich habe versucht, den Code in die isAdded()Methode einzufügen, um zu überprüfen, ob das Fragment derzeit zu seiner Aktivität hinzugefügt wird, aber ich erhalte diesen Fehler immer noch selten. Ich verstehe nicht, warum ich diesen Fehler immer noch erhalte. Wie kann ich das verhindern?

Es zeigt Fehler in der Zeile

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

Unten ist der Beispiel-API-Aufruf, den ich mache.

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();
                        }
                    }
                });
Android-Entwickler
quelle
cameraInfo.setId (getActivity (). getResources (). getString (R.string.camera_id));
Ashwin H

Antworten:

203

Dieser Fehler tritt aufgrund der kombinierten Wirkung zweier Faktoren auf:

  • Wenn die HTTP-Anforderung abgeschlossen ist, wird entweder onResponse()oder onError()(die auf dem Hauptthread funktionieren) aufgerufen, ohne zu wissen, ob sich die Activitynoch im Vordergrund befindet oder nicht. Wenn das Activityweg ist (der Benutzer hat woanders navigiert), wird getActivity()null zurückgegeben.
  • Der Volley Responsewird als anonyme innere Klasse ausgedrückt, die implizit einen starken Bezug zur äußeren ActivityKlasse hat. Dies führt zu einem klassischen Speicherverlust.

Um dieses Problem zu lösen, sollten Sie immer Folgendes tun:

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

    // etc ...

}

und auch isAdded()in der onError()Methode verwenden:

@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
quelle
2
Wenn Sie Volley-Anfragen und AsyncTasks aus einem heraus verwenden Activity, gibt es keine narrensichere Möglichkeit, NPEs zu vermeiden. Es besteht immer die Möglichkeit, dass der Benutzer von der aktuellen Seite weg navigiert, Activitywährend einer der Threads etwas im Hintergrund ausführt. Wenn der Thread abgeschlossen ist und / onPostExecute()oder onResponse()aufgerufen wird, gibt es keine Activity. Alles, was Sie tun können, ist, an verschiedenen Stellen in Ihrem Code nach
YS
2
Der Android Monkey Test (ADB Shell Monkey) ist wirklich gut darin, diesen Fehler auszurotten, wenn Sie ihn noch nicht generisch / global berücksichtigt haben.
Groovee60
5
das isAdded () ist genug, der endgültige öffentliche Boolesche Wert isAdded () {return mActivity! = null && mAdded; }
Lannyf
2
@ruselli: Überprüft das addedboolesche Flag und ob die aktuelle ActivityInstanz ist nulloder nicht.
YS
1
@gauravjain Vermeiden Sie asynchrone Anforderungen (wie HTTP-Aufrufe) direkt von Fragmenten. Tun Sie es aus der Aktivität und es sollte in Ordnung sein. Das Löschen von Fragmentreferenzen aus dem FragmentManager ist eine gute Vorgehensweise und der beste Weg, um Speicherlecks zu vermeiden.
YS
56

Der Fragmentlebenszyklus ist sehr komplex und voller Fehler. Versuchen Sie Folgendes hinzuzufügen:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}
Miroslav Michalec
quelle
2
Wo soll ich es hinstellen?
Vaclovas Rekašius Jr.
1
@ VaclovasRekašiusJr. Scheint so ziemlich überall dort zu sein, wo Sie von innerhalb des Fragments auf die Aktivität zugreifen möchten. Spaß!
TylerJames
2
Was soll ich tun, wenn Aktivität == null. um meine Bewerbung am Leben zu erhalten @Miroslav
pavel
Schauen Sie in die isAdded () , Sie haben möglicherweise festgestellt, dass "activity! = Null" nicht redundant ist
BertKing
@BertKing return mHost != null && mAdded;- Das ist es, was in der fragment.isAdded () -Methode enthalten ist. Ich dachte, dass mHost eine Aktivität ist, wenn Sie sie verfolgen, aber es scheint, dass sich mHost in FragmentActivity befindet. Also wahrscheinlich hast du recht. Irgendwelche Ergänzungen?
Johnny Five
14

Ich habe eine sehr einfache Lösung gefunden. Die Methode isAdded () ist eine der Fragmentmethoden, mit der festgestellt werden kann , ob dieses aktuelle Fragment an seine Aktivität angehängt ist oder nicht.

wir können dies wie überall in Fragmentklassen verwenden wie:

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
quelle
12

Ausnahme: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} nicht an Aktivität angehängt

Kategorie: Lebenszyklus

Beschreibung : Bei zeitaufwändigen Vorgängen im Hintergrundthread (z. B. AsyncTask) wurde in der Zwischenzeit ein neues Fragment erstellt, das vor Abschluss des Hintergrundthreads von der Aktivität getrennt wurde. Der Code im UI-Thread (z. B. onPostExecute) ruft ein getrenntes Fragment auf und löst eine solche Ausnahme aus.

Fix Lösung:

  1. Brechen Sie den Hintergrund-Thread ab, wenn Sie das Fragment anhalten oder stoppen

  2. Verwenden Sie isAdded (), um zu überprüfen, ob das Fragment angehängt ist, und um dannResources () aus der Aktivität abzurufen.

Rahil Ali
quelle
11

Ich kann zu spät kommen, aber jemandem helfen ..... Die beste Lösung hierfür ist, eine globale Anwendungsklasseninstanz zu erstellen und sie in dem bestimmten Fragment aufzurufen, an das Ihre Aktivität nicht angehängt ist

wie unten

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

Hier ist die Anwendungsklasse

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
quelle
1
Ja, diese Methode ist auch weit verbreitet.
CoolMind
1
Dies ist keine kluge Option. FragmentContext und ApplicationContext haben unterschiedliche Stile. Der Fragmentkontext kann ein dunkles Thema, einen benutzerdefinierten Stil, ein Gebietsschema usw. haben, wodurch die Farbe und die Zeichenfolgenressourcen aus verschiedenen Dateien abgerufen werden. Während ApplicationContext möglicherweise nicht die richtige Ressource abruft. Wenn Sie keinen Kontext haben, sollten Sie nicht versuchen, diese Ressource zu rendern.
Jemshit Iskenderov
2

Dieser Fehler kann auftreten, wenn Sie ein Fragment instanziieren, das irgendwie nicht instanziiert werden kann:

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

In meinem Fall habe ich dies getroffen, als ich versuchte zu verwenden:

private String loading = getString(R.string.loading);
Sagits
quelle
2

Bei der Verwendung von Fragmenten isAdded() wird true zurückgegeben, wenn das Fragment derzeit an Activity angehängt ist.

Wenn Sie in der Aktivität nachsehen möchten

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

Hoffe es wird jemandem helfen

Prateek218
quelle
1

Ich habe den folgenden Ansatz zur Behandlung dieses Problems gewählt. Erstellt eine neue Klasse, die als Wrapper für solche Aktivitätsmethoden fungiert

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

}

Wo immer ich auf Ressourcen aus Fragmenten oder Aktivitäten zugreifen muss, anstatt die Methode direkt aufzurufen, verwende ich diese Klasse. Wenn die Aktivität contextnicht aktiv ist null, wird der Wert des Assets zurückgegeben, und wenn der contextWert null ist, wird ein Standardwert übergeben (der auch vom Aufrufer der Funktion angegeben wird).

Wichtig Dies ist keine Lösung. Dies ist eine effektive Methode, um diesen Absturz ordnungsgemäß zu bewältigen. Sie möchten einige Protokolle hinzufügen, wenn Sie die Aktivitätsinstanz als null erhalten, und versuchen, dies nach Möglichkeit zu beheben.

Ezio
quelle
0

Dies geschieht, wenn das Fragment keinen Kontext hat und die Methode getActivity () daher null zurückgibt. Überprüfen Sie, ob Sie den Kontext verwenden, bevor Sie ihn erhalten , oder ob die Aktivität nicht mehr vorhanden ist. Verwenden Sie den Kontext in fragment.onCreate und nach der API-Antwort tritt dieses Problem normalerweise auf

Dexian Fan
quelle
0

Manchmal wird diese Ausnahme durch einen Fehler in der Implementierung der Support-Bibliothek verursacht. Vor kurzem musste ich ein Downgrade von 26.1.0 auf 25.4.0 durchführen, um es loszuwerden.

Bord81
quelle
Nein, ich nicht, aber vielleicht sollte ich eine erstellen.
Bord81
0

Dieses Problem tritt immer dann auf, wenn Sie einen Kontext aufrufen, der nicht verfügbar ist, oder null, wenn Sie ihn aufrufen. Dies kann eine Situation sein, in der Sie den Kontext des Hauptaktivitätsthreads in einem Hintergrundthread oder den Kontext des Hintergrundthreads in einem Hauptaktivitätsthread aufrufen.

Zum Beispiel habe ich meine gemeinsame Einstellungszeichenfolge wie folgt aktualisiert.

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

Und gleich danach finish () aufgerufen. Jetzt läuft das Commit auf dem Hauptthread und stoppt alle anderen Async-Commits, wenn es bis zum Ende kommt. Der Kontext bleibt also lebendig, bis der Schreibvorgang abgeschlossen ist. Daher ist der vorherige Kontext aktiv, wodurch der Fehler auftritt.

Stellen Sie daher sicher, dass Ihr Code erneut überprüft wird, wenn Code mit diesem Kontextproblem vorliegt.

Prashant Paliwal
quelle
Wie haben Sie es geschafft, dieses Problem zu beheben? Ich rufe es in einem asynchronen Thread auf und habe dieses Problem jetzt.
Simon
Stellen Sie einfach sicher, dass der Schreibvorgang abgeschlossen ist. Dann wird nur der Kontext beendet, bevor der Schreibvorgang abgeschlossen ist.
Prashant Paliwal