java.lang.IllegalStateException: fragment nie jest dołączony do działania

148

Rzadko otrzymuję ten błąd podczas wykonywania wywołania interfejsu API.

java.lang.IllegalStateException: Fragment  not attached to Activity

Próbowałem umieścić kod w isAdded()metodzie, aby sprawdzić, czy fragment jest aktualnie dodawany do jej aktywności, ale nadal rzadko pojawia się ten błąd. Nie rozumiem, dlaczego nadal otrzymuję ten błąd. Jak mogę temu zapobiec?

Pokazuje błąd na linii

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

Poniżej znajduje się przykładowe wywołanie interfejsu API, które tworzę.

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();
                        }
                    }
                });
Programista Androida
źródło
cameraInfo.setId (getActivity (). getResources (). getString (R.string.camera_id));
Ashwin H,

Odpowiedzi:

203

Ten błąd występuje z powodu połączonego wpływu dwóch czynników:

  • Żądanie HTTP, kiedy kompletne, albo wywołuje onResponse()lub onError()(które działają w głównym wątku), nie wiedząc, czy Activitynadal jest na pierwszym planie, czy nie. Jeśli Activityzniknął (użytkownik przeszedł gdzie indziej), getActivity()zwraca null.
  • Volley Responsejest wyrażona jako anonimowa klasa wewnętrzna, która domyślnie zawiera silne odniesienie do Activityklasy zewnętrznej . Powoduje to klasyczny wyciek pamięci.

Aby rozwiązać ten problem, zawsze powinieneś:

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

    // etc ...

}

a także użyj isAdded()w onError()metodzie:

@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
źródło
2
W przypadku korzystania z żądań i AsyncTasks Volley z wewnątrz obiektu Activitynie ma niezawodnego sposobu uniknięcia NPE. Zawsze istnieje szansa, że ​​użytkownik może opuścić bieżący, Activitypodczas gdy jeden z wątków robi coś w tle, a następnie, gdy wątek zakończy się i / onPostExecute()lub onResponse()zostanie wywołany, nie ma Activity. Wszystko, co możesz zrobić, to sprawdzić zerowe odwołania w różnych punktach kodu, a to nie jest kuloodporne :)
YS
2
Test małpy Androida (małpa skorupy adb) jest naprawdę dobry w wykorzenieniu tego błędu, jeśli jeszcze go nie uwzględniono w sposób ogólny / globalny.
Groovee60
5
wystarczy isAdded (), ostateczna publiczna wartość logiczna isAdded () {return mActivity! = null && mAdded; }
lannyf
2
@ruselli: sprawdza addedflagę logiczną i czy bieżąca Activityinstancja jest, nullczy nie.
YS,
1
@gauravjain Unikaj wykonywania żądań asynchronicznych (takich jak wywołania HTTP) bezpośrednio z fragmentów. Zrób to z Aktywności i powinno być dobrze. Wyczyść również odwołania fragmentów z FragmentManager, jest to dobra praktyka i najlepszy sposób na uniknięcie wycieków pamięci.
YS
56

Cykl życia fragmentu jest bardzo złożony i pełen błędów, spróbuj dodać:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}
Miroslav Michalec
źródło
2
Gdzie mam to położyć?
Vaclovas Rekašius Jr.
1
@ VaclovasRekašiusJr. Wydaje się, że prawie wszędzie chcesz uzyskać dostęp do działania z wnętrza fragmentu. Zabawa!
TylerJames
2
co powinienem zrobić, jeśli aktywność == null. to keep alive my application @Miroslav
pavel
Zajrzyj do isAdded () , może się okazać, że „activity! = Null” nie jest zbędny
BertKing
@BertKing return mHost != null && mAdded;- To właśnie jest w metodzie fragment.isAdded (). Myślałem, że mHost jest działaniem, jeśli go prześledzisz, ale wygląda na to, że mHost znajduje się wewnątrz FragmentActivity. Więc prawdopodobnie masz rację. Jakieś dodatki?
Johnny Five
14

Znalazłem bardzo proste rozwiązanie metoda isAdded () , która jest jedną z metod fragmentów służących do identyfikacji, czy ten bieżący fragment jest dołączony do jego działania, czy nie.

możemy tego użyć wszędzie w klasie fragmentów, takiej jak:

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
źródło
12

Wyjątek: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} nie jest dołączone do działania

Kategoria: Cykl życia

Opis : podczas wykonywania czasochłonnej operacji w wątku w tle (np. AsyncTask) w międzyczasie utworzono nowy fragment i odłączono go od działania przed zakończeniem wątku w tle. Kod w wątku UI (np. OnPostExecute) wywołuje odłączony fragment, zgłaszając taki wyjątek.

Napraw rozwiązanie:

  1. Anuluj wątek w tle podczas wstrzymywania lub zatrzymywania fragmentu

  2. Użyj isAdded (), aby sprawdzić, czy fragment jest dołączony, a następnie wykonaj getResources () z działania.

Rahil Ali
źródło
11

mogę się spóźnić, ale mogę komuś pomóc ..... Najlepszym rozwiązaniem jest utworzenie globalnej instancji klasy aplikacji i wywołanie jej w konkretnym fragmencie, do którego nie jest dołączana Twoja aktywność

jak poniżej

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

Oto klasa aplikacji

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
źródło
1
Tak, ta metoda jest również szeroko stosowana.
CoolMind,
1
To nie jest mądra opcja. FragmentContext i ApplicationContext mają różne style. Kontekst fragmentu może mieć ciemny motyw, niestandardowy styl, ustawienia regionalne itp., Które będą pobierać kolory i zasoby ciągów z różnych plików. Chociaż ApplicationContext może nie ściągać prawidłowego zasobu. Jeśli nie masz kontekstu, nie powinieneś próbować renderować tego zasobu.
Jemshit Iskenderov
2

Ten błąd może się zdarzyć, jeśli tworzysz wystąpienie fragmentu, którego w jakiś sposób nie można utworzyć:

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

W moim przypadku spotkałem się z tym, gdy próbowałem użyć:

private String loading = getString(R.string.loading);
zwisy
źródło
2

W użyciu fragmentów isAdded() Zwróci wartość true, jeśli fragment jest obecnie dołączony do działania.

Jeśli chcesz sprawdzić wewnątrz działania

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

Mam nadzieję, że to komuś pomoże

Prateek218
źródło
1

Przyjąłem następujące podejście do rozwiązania tego problemu. Utworzono nową klasę, która działa jako opakowanie dla metod działań takich jak ta

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

}

Teraz wszędzie tam, gdzie potrzebuję dostępu do zasobów z fragmentów lub działań, zamiast bezpośrednio wywoływać metodę, używam tej klasy. W przypadku, gdy działanie contextnie nulljest, zwraca wartość aktywa, aw przypadku, gdycontext jest równe null, przekazuje wartość domyślną (która jest również określana przez wywołującego funkcję).

Ważny To nie jest rozwiązanie, jest to skuteczny sposób, aby z wdziękiem poradzić sobie z tą awarią. Chciałbyś dodać niektóre dzienniki w przypadkach, w których instancja aktywności ma wartość Null i spróbuj to naprawić, jeśli to możliwe.

Ezio
źródło
0

Dzieje się tak, gdy fragment nie ma kontekstu, dlatego metoda getActivity () zwraca wartość null. sprawdź, czy używasz kontekstu, zanim go otrzymasz get lub czy działanie już nie istnieje. użyj kontekstu w fragment.onCreate i po odpowiedzi api zwykle powoduje ten problem

dexian fan
źródło
0

Czasami ten wyjątek jest spowodowany błędem w implementacji biblioteki obsługi. Ostatnio musiałem obniżyć wersję z 26.1.0 do 25.4.0, aby się go pozbyć.

Bord81
źródło
Nie, nie wiem, ale może powinienem taki stworzyć.
Bord81
0

Ten problem występuje za każdym razem, gdy wywołujesz kontekst, który jest niedostępny lub zerowy. Może to być sytuacja, gdy wywołujesz kontekst wątku głównego działania w wątku w tle lub kontekst wątku w tle w wątku głównym działania.

Na przykład zaktualizowałem mój udostępniony ciąg preferencji, taki jak następujący.

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

I zaraz po nim wywołał finish (). Teraz to, co robi, to to, że jako zatwierdzenie działa w głównym wątku i zatrzymuje wszelkie inne zatwierdzenia Async, jeśli nadchodzą, dopóki nie zakończy się. Więc jego kontekst jest żywy, dopóki zapis nie zostanie zakończony. W związku z tym poprzedni kontekst jest aktywny, powodując wystąpienie błędu.

Dlatego upewnij się, że kod został ponownie sprawdzony, jeśli jest jakiś kod, który ma ten problem z kontekstem.

Prashant Paliwal
źródło
jak udało ci się rozwiązać ten problem? Nazywam to w ramach wątku asynchronicznego i teraz doświadczam tego problemu.
Simon
Po prostu upewnij się, że operacja zapisu została zakończona, wtedy tylko kontekst jest zabijany nie przed zakończeniem operacji zapisu.
Prashant Paliwal