java.lang.IllegalStateException: frammento non attaccato all'attività


148

Raramente ricevo questo errore durante una chiamata API.

java.lang.IllegalStateException: Fragment  not attached to Activity

Ho provato a inserire il codice all'interno del isAdded()metodo per verificare se il frammento è attualmente aggiunto alla sua attività, ma raramente ottengo questo errore. Non riesco a capire perché sto ancora ricevendo questo errore. Come posso prevenirlo?

Sta mostrando l'errore sulla linea-

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

Di seguito è la chiamata API di esempio che sto effettuando.

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();
                        }
                    }
                });

cameraInfo.setId (.. getActivity () getResources () getString (R.string.camera_id));
Ashwin H,

Risposte:


203

Questo errore si verifica a causa dell'effetto combinato di due fattori:

  • La richiesta HTTP, una volta completata, richiama onResponse()o onError()(che funziona sul thread principale) senza sapere se Activityè ancora in primo piano o meno. Se Activityè sparito (l'utente ha navigato altrove), getActivity()restituisce null.
  • Il Volley Responseè espresso come una classe interna anonima, che contiene implicitamente un forte riferimento alla Activityclasse esterna . Ciò si traduce in una classica perdita di memoria.

Per risolvere questo problema, dovresti sempre fare:

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

    // etc ...

}

e utilizzare anche isAdded()nel onError()metodo:

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

2
Quando si utilizzano le richieste Volley AsyncTaskall'interno di un Activity, non esiste un modo infallibile per evitare gli NPE. C'è sempre la possibilità che l'utente possa allontanarsi dalla corrente Activitymentre uno dei thread sta facendo qualcosa in background, e quindi quando il thread si completa e onPostExecute()o onResponse()viene chiamato, non c'è Activity. Tutto quello che puoi fare è verificare i riferimenti null in vari punti del codice e questo non è a prova di proiettile :)
YS

2
Il test delle scimmie Android (adb shell monkey) è davvero bravo a sradicare questo errore se non l'hai già spiegato in modo generico / globale.
Groovee60,

5
isAdded () è sufficiente, il valore booleano pubblico finale isAdded () {return mActivity! = null && mAdded; }
Lannyf,

2
@ruselli: controlla il addedflag booleano e se l' Activityistanza corrente è nullo meno.

1
@gauravjain Evita di fare richieste asincrone (come le chiamate HTTP) direttamente dai frammenti. Fallo dall'attività e dovrebbe andare bene. Inoltre, cancella i riferimenti ai frammenti dal FragmentManager, è una buona pratica ed è il modo migliore per evitare perdite di memoria.

56

Il ciclo di vita dei frammenti è molto complesso e pieno di bug, prova ad aggiungere:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}

2
Dove dovrei metterlo?
Vaclovas Rekašius Jr.

1
@ VaclovasRekašiusJr. Sembra praticamente ovunque tu voglia accedere all'attività dall'interno del frammento. Divertimento!
TylerJames,

2
cosa devo fare se attività == null. per mantenere viva la mia domanda @ Miroslav
pavel

Guarda in isAdded () , potresti trovare "attività! = Null" non ridondante
BertKing

@BertKing return mHost != null && mAdded;: ecco cosa è all'interno del metodo fragment.isAdded (). Ho pensato che mHost sia un'attività se la rintracci, ma sembra che mHost sia all'interno di FragmentActivity. Quindi, probabilmente, hai ragione. Eventuali aggiunte?
Johnny Five,

14

Ho trovato il metodo isAdded () Very Simple Solution che è uno dei metodi del frammento per identificare che questo frammento corrente è collegato alla sua attività o meno.

possiamo usarlo come ovunque nella classe dei frammenti come:

if(isAdded())
{

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

}

12

Eccezione: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} non allegato all'attività

Categoria: Ciclo di vita

Descrizione : quando si esegue un'operazione che richiede tempo nel thread in background (ad esempio, AsyncTask), nel frattempo è stato creato un nuovo frammento che è stato rimosso dall'attività prima che il thread in background fosse terminato. Il codice nel thread dell'interfaccia utente (ad esempio, onPostExecute) chiama un frammento distaccato, generando tale eccezione.

Soluzione di correzione:

  1. Annullare il thread di sfondo quando si mette in pausa o si interrompe il frammento

  2. Utilizzare isAdded () per verificare se il frammento è collegato e quindi per getResources () dall'attività.


11

potrei essere in ritardo ma posso aiutare qualcuno ..... La soluzione migliore per questo è creare un'istanza di classe di applicazione globale e chiamarla nel frammento particolare in cui la tua attività non viene collegata

come sotto

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

Ecco la classe di applicazione

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

1
Sì, questo metodo è anche ampiamente usato.
CoolMind

1
Questa non è un'opzione saggia. FragmentContext e ApplicationContext hanno stili diversi. Frammento Il contesto può avere temi scuri, stile personalizzato, impostazioni internazionali, ecc. Che estrarranno il colore, le risorse di stringa da file diversi. Mentre ApplicationContext potrebbe non estrarre la risorsa corretta. Se non hai il contesto, non dovresti provare a renderizzare quella risorsa.
Jemshit Iskenderov,

2

Questo errore può verificarsi se si crea un'istanza di un frammento che in qualche modo non può essere istanziato:

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

Nel mio caso, ho incontrato questo quando ho provato a usare:

private String loading = getString(R.string.loading);

2

Nell'uso del frammento isAdded() Restituirà vero se il frammento è attualmente collegato all'attività.

Se vuoi controllare all'interno dell'Attività

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

Spero che possa aiutare qualcuno


1

Ho adottato il seguente approccio per gestire questo problema. Creata una nuova classe che funge da wrapper per metodi di attività come questo

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

}

Ora, ovunque ho bisogno di accedere alle risorse da frammenti o attività, invece di chiamare direttamente il metodo, uso questa classe. Nel caso in cui l'attività contextnon nulllo sia, restituisce il valore dell'asset e nel caso in cui contextsia null, passa un valore predefinito (che viene anche specificato dal chiamante della funzione).

Importante Questa non è una soluzione, questo è un modo efficace in cui è possibile gestire questo arresto con grazia. Si desidera aggiungere alcuni registri nei casi in cui si ottiene l'istanza di attività come null e provare a risolvere il problema, se possibile.


0

questo accade quando il frammento non ha un contesto, quindi il metodo getActivity () restituisce null. controlla se usi il contesto prima di averlo , o se l'attività non esiste più. usa il contesto in fragment.onCreate e dopo la risposta api in genere causa questo problema


0

A volte questa eccezione è causata da un bug nell'implementazione della libreria di supporto. Di recente ho dovuto effettuare il downgrade dalla 26.1.0 alla 25.4.0 per liberarmene.


No, no, ma forse dovrei crearne uno.
Bord81,

0

Questo problema si verifica ogni volta che chiami un contesto che non è disponibile o nullo quando lo chiami. Ciò può verificarsi quando si chiama il contesto del thread attività principale su un thread in background o il contesto del thread in background sul thread attività principale.

Ad esempio, ho aggiornato la mia stringa di preferenze condivisa come segue.

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

E ha chiamato finish () subito dopo. Ora quello che fa è che come commit viene eseguito sul thread principale e interrompe qualsiasi altro commit Async se arriva fino al termine. Quindi il suo contesto è vivo fino al completamento della scrittura. Quindi il contesto precedente è attivo, causando l'errore.

Quindi assicurati di ricontrollare il tuo codice se c'è qualche codice con questo problema di contesto.


come sei riuscito a risolvere questo problema? Lo sto chiamando all'interno di un thread asincrono e sto riscontrando questo problema ora.
Simon,

Assicurati solo che l'operazione di scrittura sia terminata, quindi solo il contesto viene ucciso non prima del completamento dell'operazione di scrittura.
Prashant Paliwal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.